1 /*
2 SPDX-FileCopyrightText: 2002 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "colorscheme.h"
8
9 #include "kspaths.h"
10 #include "ksutils.h"
11 #include "Options.h"
12 #include "auxiliary/ksnotification.h"
13 #include "skyobjects/starobject.h"
14 #ifdef KSTARS_LITE
15 #include "skymaplite.h"
16 #else
17 #include "skyqpainter.h"
18 #endif
19
ColorScheme()20 ColorScheme::ColorScheme() : FileName()
21 {
22 //Each color has two names associated with it. The KeyName is its
23 //identification in the QMap, the *.colors file, and the config file.
24 //The Name is what appears in the ViewOpsDialog ListBox.
25 //In addition, we define default RGB strings for each item.
26 //To add another color to the Palette, just add an entry for KeyName,
27 //Name and Default here.
28
29 appendItem("SkyColor", i18n("Sky"), "#000000");
30 appendItem("DSOColor", i18n("Messier Object"), "#008f00");
31 appendItem("HSTColor", i18nc("Object with extra attached URLs", "Object w/ Links"), "#930000");
32 appendItem("SNameColor", i18n("Star Name"), "#577d7d");
33 appendItem("DSNameColor", i18n("Deep Sky Object Name"), "#75759c");
34 appendItem("PNameColor", i18n("Planet Name"), "#ac9800");
35 appendItem("CNameColor", i18nc("Constellation Name", "Constell. Name"), "#718488");
36 appendItem("CLineColor", i18nc("Constellation Line", "Constell. Line"), "#3d3d3d");
37 appendItem("CBoundColor", i18nc("Constellation Boundary", "Constell. Boundary"), "#222f2f");
38 appendItem("CBoundHighColor", i18nc("Highlighted Constellation Boundary", "Constell. Boundary Highlight"),
39 "#445555");
40 appendItem("MWColor", i18nc("refers to the band of stars in the sky due to the Galactic plane", "Milky Way"),
41 "#0d1115");
42 appendItem("EqColor", i18n("Equator"), "#909090");
43 appendItem("EclColor", i18n("Ecliptic"), "#613d12");
44 appendItem("HorzColor", i18n("Horizon"), "#091f14");
45 appendItem("LocalMeridianColor", i18n("Local Meridian"), "#0059b3");
46 appendItem("CompassColor", i18n("Compass Labels"), "#909055");
47 appendItem("EquatorialGridColor", i18n("Equatorial Coordinate Grid"), "#445566");
48 appendItem("HorizontalGridColor", i18n("Horizontal Coordinate Grid"), "#091f14");
49 appendItem("BoxTextColor", i18n("Info Box Text"), "#d2dbef");
50 appendItem("BoxGrabColor", i18n("Info Box Selected"), "#900000");
51 appendItem("BoxBGColor", i18n("Info Box Background"), "#000000");
52 appendItem("TargetColor", i18n("Target Indicator"), "#DD0000");
53 appendItem("UserLabelColor", i18n("User Labels"), "#AAAAAA");
54 appendItem("PlanetTrailColor", i18n("Planet Trails"), "#993311");
55 appendItem("AngularRuler", i18n("Angular Distance Ruler"), "#445566");
56 appendItem("ObsListColor", i18n("Observing List Label"), "#FF0000");
57 appendItem("StarHopRouteColor", i18n("Star-Hop Route"), "#00FFFF");
58 appendItem("VisibleSatColor", i18n("Visible Satellites"), "#00FF00");
59 appendItem("SatColor", i18n("Satellites"), "#FF0000");
60 appendItem("SatLabelColor", i18n("Satellites Labels"), "#640000");
61 appendItem("SupernovaColor", i18n("Supernovae"), "#FFA500");
62 appendItem("ArtificialHorizonColor", i18n("Artificial Horizon"), "#C82828");
63 appendItem("RAGuideError", i18n("RA Guide Error"), "#00FF00");
64 appendItem("DEGuideError", i18n("DEC Guide Error"), "#00A5FF");
65 appendItem("SolverFOVColor", i18n("Solver FOV"), "#FFFFFF");
66 appendItem("SensorFOVColor", i18n("Sensor FOV"), "#FFAA00");
67 appendItem("HIPSGridColor", i18n("HiPS Grid"), "#FFFFFF");
68 appendItem("FITSObjectLabelColor", i18n("FITS Image Object Label"), "#00FF00");
69
70 //Load colors from config object
71 loadFromConfig();
72
73 //Default values for integer variables:
74 StarColorMode = 0;
75 StarColorIntensity = 4;
76 DarkPalette = 0;
77 }
78
appendItem(const QString & key,const QString & name,const QString & def)79 void ColorScheme::appendItem(const QString &key, const QString &name, const QString &def)
80 {
81 KeyName.append(key);
82 Name.append(name);
83 Default.append(def);
84 }
85
colorNamed(const QString & name) const86 QColor ColorScheme::colorNamed(const QString &name) const
87 {
88 if (!hasColorNamed(name))
89 {
90 qWarning() << "No color named" << name << "found in color scheme.";
91 // Return white if no color found
92 return QColor(Qt::white);
93 }
94 return QColor(Palette[name]);
95 }
96
colorAt(int i) const97 QColor ColorScheme::colorAt(int i) const
98 {
99 return QColor(Palette[KeyName.at(i)]);
100 }
101
nameAt(int i) const102 QString ColorScheme::nameAt(int i) const
103 {
104 return Name.at(i);
105 }
106
keyAt(int i) const107 QString ColorScheme::keyAt(int i) const
108 {
109 return KeyName.at(i);
110 }
111
nameFromKey(const QString & key) const112 QString ColorScheme::nameFromKey(const QString &key) const
113 {
114 return nameAt(KeyName.indexOf(key));
115 }
116
setColor(const QString & key,const QString & color)117 void ColorScheme::setColor(const QString &key, const QString &color)
118 {
119 //We can blindly insert() the new value; if the key exists, the old value is replaced
120 Palette.insert(key, color);
121
122 KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
123 cg.writeEntry(key, color);
124 }
125
load(const QString & name)126 bool ColorScheme::load(const QString &name)
127 {
128 QString filename = name.toLower().trimmed();
129 QFile file;
130 int inew = 0, iold = 0;
131 bool ok = false;
132
133 //Parse default names which don't follow the regular file-naming scheme
134 if (name == i18nc("use default color scheme", "Default Colors"))
135 filename = "classic.colors";
136 if (name == i18nc("use 'star chart' color scheme", "Star Chart"))
137 filename = "chart.colors";
138 if (name == i18nc("use 'night vision' color scheme", "Night Vision"))
139 filename = "night.colors";
140
141 //Try the filename if it ends with ".colors"
142 if (filename.endsWith(QLatin1String(".colors")))
143 ok = KSUtils::openDataFile(file, filename);
144
145 //If that didn't work, try assuming that 'name' is the color scheme name
146 //convert it to a filename exactly as ColorScheme::save() does
147 if (!ok)
148 {
149 if (!filename.isEmpty())
150 {
151 filename.replace(' ', '-').append(".colors");
152 ok = KSUtils::openDataFile(file, filename);
153 }
154
155 if (!ok)
156 {
157 qDebug() << QString("Unable to load color scheme named %1. Also tried %2.").arg(name, filename);
158 return false;
159 }
160 }
161
162 //If we reach here, the file should have been successfully opened
163 QTextStream stream(&file);
164
165 //first line is the star-color mode and star color intensity and dark palette
166 QString line = stream.readLine();
167 QStringList modes = line.split(':');
168
169 // Star Color Mode
170 if (modes.count() > 0)
171 {
172 int newmode = modes[0].toInt(&ok);
173 if (ok)
174 setStarColorMode(newmode);
175 }
176
177 // Star Intensity
178 if (modes.count() > 1)
179 {
180 int newintens = modes[1].toInt(&ok);
181 if (ok)
182 setStarColorIntensity(newintens);
183 }
184
185 // Dark Palette
186 #if 0
187 if (modes.count() > 2)
188 {
189 int newintens = modes[2].toInt(&ok);
190 if (ok)
191 setDarkPalette(newintens == 1);
192 }
193 #endif
194
195 //More flexible method for reading in color values. Any order is acceptable, and
196 //missing entries are ignored.
197 while (!stream.atEnd())
198 {
199 line = stream.readLine();
200
201 if (line.count(':') ==
202 1) //the new color preset format contains a ":" in each line, followed by the name of the color
203 {
204 ++inew;
205 if (iold)
206 return false; //we read at least one line without a colon...file is corrupted.
207
208 //If this line has a valid Key, set the color.
209 QString tkey = line.mid(line.indexOf(':') + 1).trimmed();
210 QString tname = line.left(line.indexOf(':') - 1);
211
212 if (KeyName.contains(tkey))
213 {
214 setColor(tkey, tname);
215 }
216 else //attempt to translate from old color ID
217 {
218 QString k(line.mid(5).trimmed() + "Color");
219 if (KeyName.contains(k))
220 {
221 setColor(k, tname);
222 }
223 else
224 {
225 qWarning() << "Could not use the key \"" << tkey << "\" from the color scheme file \"" << filename
226 << "\". I also tried \"" << k << "\".";
227 }
228 }
229 }
230 else // no ':' seen in the line, so we must assume the old format
231 {
232 ++iold;
233 if (inew)
234 return false; //a previous line had a colon, this line doesn't. File is corrupted.
235
236 //Assuming the old *.colors format. Loop through the KeyName list,
237 //and assign each color. Note that order matters here, but only here
238 //(so if you don't use the old format, then order doesn't ever matter)
239 foreach (const QString &key, KeyName)
240 setColor(key, line.left(7));
241 }
242 }
243
244 FileName = filename;
245 return true;
246 }
247
save(const QString & name)248 bool ColorScheme::save(const QString &name)
249 {
250 QFile file;
251
252 //Construct a file name from the scheme name. Make lowercase, replace spaces with "-",
253 //and append ".colors".
254 QString filename = name.toLower().trimmed();
255 if (!filename.isEmpty())
256 {
257 for (int i = 0; i < filename.length(); ++i)
258 if (filename.at(i) == ' ')
259 filename.replace(i, 1, "-");
260
261 filename = filename.append(".colors");
262 //determine filename in local user KDE directory tree.
263 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath(filename));
264
265 //if (file.exists() || !file.open(QIODevice::ReadWrite | QIODevice::Append))
266 if (!file.open(QIODevice::ReadWrite))
267 {
268 KSNotification::sorry(i18n("Local color scheme file could not be opened.\nScheme cannot be recorded."));
269 return false;
270 }
271 else
272 {
273 QTextStream stream(&file);
274 stream << StarColorMode << ":" << StarColorIntensity << ":" << DarkPalette << '\n';
275
276 foreach (const QString &key, KeyName)
277 stream << Palette[key] << " :" << key << '\n';
278 file.close();
279 }
280
281 //determine filename in local user KDE directory tree.
282 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("colors.dat"));
283
284 if (!file.open(QIODevice::ReadWrite))
285 {
286 KSNotification::sorry(i18n("Local color scheme index file could not be opened.\nScheme cannot be recorded."));
287 }
288 else
289 {
290 bool found = false;
291 QString schemeLine = name + ':' + filename;
292
293 // Check if the scheme line is in the colors.dat file
294 // If not, then we add it
295 QTextStream stream(&file);
296 while (stream.atEnd() == false)
297 {
298 QString line = stream.readLine();
299 if (line == schemeLine)
300 {
301 found = true;
302 break;
303 }
304 }
305
306 if (found == false)
307 {
308 stream << schemeLine << '\n';
309 file.close();
310 }
311 }
312 }
313 else
314 {
315 KSNotification::sorry("Invalid filename requested.\nScheme cannot be recorded.");
316 return false;
317 }
318
319 FileName = filename;
320 saveToConfig();
321 return true;
322 }
323
loadFromConfig()324 void ColorScheme::loadFromConfig()
325 {
326 KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
327
328 for (int i = 0; i < KeyName.size(); ++i)
329 setColor(KeyName.at(i), cg.readEntry(KeyName.at(i).toUtf8().constData(), Default.at(i)));
330
331 setStarColorModeIntensity(cg.readEntry("StarColorMode", 0), cg.readEntry("StarColorIntensity", 5));
332 //setDarkPalette(cg.readEntry("DarkAppColors", false));
333
334 FileName = cg.readEntry("ColorSchemeFile", "moonless-night.colors");
335 }
336
saveToConfig()337 void ColorScheme::saveToConfig()
338 {
339 KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
340 for (int i = 0; i < KeyName.size(); ++i)
341 {
342 QString c = colorNamed(KeyName.at(i)).name();
343 cg.writeEntry(KeyName.at(i), c);
344 }
345
346 cg.writeEntry("StarColorMode", starColorMode());
347 cg.writeEntry("StarColorIntensity", starColorIntensity());
348 cg.writeEntry("ColorSchemeFile", FileName);
349 cg.writeEntry("DarkAppColors", useDarkPalette());
350 }
351
setStarColorMode(int mode)352 void ColorScheme::setStarColorMode(int mode)
353 {
354 StarColorMode = mode;
355 Options::setStarColorMode(mode);
356 #ifndef KSTARS_LITE
357 SkyQPainter::initStarImages();
358 #endif
359 }
360
361 #if 0
362 void ColorScheme::setDarkPalette(bool enable)
363 {
364 DarkPalette = enable ? 1 : 0;
365 Options::setDarkAppColors(enable);
366 #ifndef KSTARS_LITE
367 SkyQPainter::initStarImages();
368 #endif
369 }
370 #endif
371
setStarColorIntensity(int intens)372 void ColorScheme::setStarColorIntensity(int intens)
373 {
374 StarColorIntensity = intens;
375 Options::setStarColorIntensity(intens);
376 #ifndef KSTARS_LITE
377 SkyQPainter::initStarImages();
378 #endif
379 }
380
setStarColorModeIntensity(int mode,int intens)381 void ColorScheme::setStarColorModeIntensity(int mode, int intens)
382 {
383 StarColorMode = mode;
384 StarColorIntensity = intens;
385 Options::setStarColorMode(mode);
386 Options::setStarColorIntensity(intens);
387 #ifndef KSTARS_LITE
388 SkyQPainter::initStarImages();
389 #endif
390 }
391