File indexing completed on 2024-04-21 14:43:56

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jason Harris <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "colorscheme.h"
0008 
0009 #include "kspaths.h"
0010 #include "ksutils.h"
0011 #include "Options.h"
0012 #include "auxiliary/ksnotification.h"
0013 #include "skyobjects/starobject.h"
0014 #ifdef KSTARS_LITE
0015 #include "skymaplite.h"
0016 #else
0017 #include "skyqpainter.h"
0018 #endif
0019 
0020 ColorScheme::ColorScheme() : FileName()
0021 {
0022     //Each color has two names associated with it.  The KeyName is its
0023     //identification in the QMap, the *.colors file, and the config file.
0024     //The Name is what appears in the ViewOpsDialog ListBox.
0025     //In addition, we define default RGB strings for each item.
0026     //To add another color to the Palette, just add an entry for KeyName,
0027     //Name and Default here.
0028 
0029     appendItem("SkyColor", i18n("Sky"), "#000000");
0030     appendItem("DSOColor", i18n("Messier Object"), "#008f00");
0031     appendItem("HSTColor", i18nc("Object with extra attached URLs", "Object w/ Links"), "#930000");
0032     appendItem("SNameColor", i18n("Star Name"), "#577d7d");
0033     appendItem("DSNameColor", i18n("Deep Sky Object Name"), "#75759c");
0034     appendItem("PNameColor", i18n("Planet Name"), "#ac9800");
0035     appendItem("CNameColor", i18nc("Constellation Name", "Constell. Name"), "#718488");
0036     appendItem("CLineColor", i18nc("Constellation Line", "Constell. Line"), "#3d3d3d");
0037     appendItem("CBoundColor", i18nc("Constellation Boundary", "Constell. Boundary"), "#222f2f");
0038     appendItem("CBoundHighColor", i18nc("Highlighted Constellation Boundary", "Constell. Boundary Highlight"),
0039                "#445555");
0040     appendItem("MWColor", i18nc("refers to the band of stars in the sky due to the Galactic plane", "Milky Way"),
0041                "#0d1115");
0042     appendItem("EqColor", i18n("Equator"), "#909090");
0043     appendItem("EclColor", i18n("Ecliptic"), "#613d12");
0044     appendItem("HorzColor", i18n("Horizon"), "#091f14");
0045     appendItem("LocalMeridianColor", i18n("Local Meridian"), "#0059b3");
0046     appendItem("CompassColor", i18n("Compass Labels"), "#909055");
0047     appendItem("EquatorialGridColor", i18n("Equatorial Coordinate Grid"), "#445566");
0048     appendItem("HorizontalGridColor", i18n("Horizontal Coordinate Grid"), "#091f14");
0049     appendItem("BoxTextColor", i18n("Info Box Text"), "#d2dbef");
0050     appendItem("BoxGrabColor", i18n("Info Box Selected"), "#900000");
0051     appendItem("BoxBGColor", i18n("Info Box Background"), "#000000");
0052     appendItem("TargetColor", i18n("Target Indicator"), "#DD0000");
0053     appendItem("UserLabelColor", i18n("User Labels"), "#AAAAAA");
0054     appendItem("PlanetTrailColor", i18n("Planet Trails"), "#993311");
0055     appendItem("AngularRuler", i18n("Angular Distance Ruler"), "#445566");
0056     appendItem("ObsListColor", i18n("Observing List Label"), "#FF0000");
0057     appendItem("StarHopRouteColor", i18n("Star-Hop Route"), "#00FFFF");
0058     appendItem("VisibleSatColor", i18n("Visible Satellites"), "#00FF00");
0059     appendItem("SatColor", i18n("Satellites"), "#FF0000");
0060     appendItem("SatLabelColor", i18n("Satellites Labels"), "#640000");
0061     appendItem("SupernovaColor", i18n("Supernovae"), "#FFA500");
0062     appendItem("AsteroidColor", i18n("Asteroids"), "#F1C232");
0063     appendItem("ArtificialHorizonColor", i18n("Artificial Horizon"), "#C82828");
0064     appendItem("RAGuideError", i18n("RA Guide Error"), "#00FF00");
0065     appendItem("DEGuideError", i18n("DEC Guide Error"), "#00A5FF");
0066     appendItem("SolverFOVColor", i18n("Solver FOV"), "#FFFFFF");
0067     appendItem("SensorFOVColor", i18n("Sensor FOV"), "#FFAA00");
0068     appendItem("HIPSGridColor", i18n("HiPS Grid"), "#FFFFFF");
0069     appendItem("FITSObjectLabelColor", i18n("FITS Image Object Label"), "#00FF00");
0070 
0071     //Load colors from config object
0072     loadFromConfig();
0073 
0074     //Default values for integer variables:
0075     StarColorMode      = 0;
0076     StarColorIntensity = 4;
0077     DarkPalette        = 0;
0078 }
0079 
0080 void ColorScheme::appendItem(const QString &key, const QString &name, const QString &def)
0081 {
0082     KeyName.append(key);
0083     Name.append(name);
0084     Default.append(def);
0085 }
0086 
0087 QColor ColorScheme::colorNamed(const QString &name) const
0088 {
0089     if (!hasColorNamed(name))
0090     {
0091         qWarning() << "No color named" << name << "found in color scheme.";
0092         // Return white if no color found
0093         return QColor(Qt::white);
0094     }
0095     return QColor(Palette[name]);
0096 }
0097 
0098 QColor ColorScheme::colorAt(int i) const
0099 {
0100     return QColor(Palette[KeyName.at(i)]);
0101 }
0102 
0103 QString ColorScheme::nameAt(int i) const
0104 {
0105     return Name.at(i);
0106 }
0107 
0108 QString ColorScheme::keyAt(int i) const
0109 {
0110     return KeyName.at(i);
0111 }
0112 
0113 QString ColorScheme::nameFromKey(const QString &key) const
0114 {
0115     return nameAt(KeyName.indexOf(key));
0116 }
0117 
0118 void ColorScheme::setColor(const QString &key, const QString &color)
0119 {
0120     //We can blindly insert() the new value; if the key exists, the old value is replaced
0121     Palette.insert(key, color);
0122 
0123     KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
0124     cg.writeEntry(key, color);
0125 }
0126 
0127 bool ColorScheme::load(const QString &name)
0128 {
0129     QString filename = name.toLower().trimmed();
0130     QFile file;
0131     int inew = 0, iold = 0;
0132     bool ok = false;
0133 
0134     //Parse default names which don't follow the regular file-naming scheme
0135     if (name == i18nc("use default color scheme", "Default Colors"))
0136         filename = "classic.colors";
0137     if (name == i18nc("use 'star chart' color scheme", "Star Chart"))
0138         filename = "chart.colors";
0139     if (name == i18nc("use 'night vision' color scheme", "Night Vision"))
0140         filename = "night.colors";
0141 
0142     //Try the filename if it ends with ".colors"
0143     if (filename.endsWith(QLatin1String(".colors")))
0144         ok = KSUtils::openDataFile(file, filename);
0145 
0146     //If that didn't work, try assuming that 'name' is the color scheme name
0147     //convert it to a filename exactly as ColorScheme::save() does
0148     if (!ok)
0149     {
0150         if (!filename.isEmpty())
0151         {
0152             filename.replace(' ', '-').append(".colors");
0153             ok = KSUtils::openDataFile(file, filename);
0154         }
0155 
0156         if (!ok)
0157         {
0158             qDebug() << Q_FUNC_INFO << QString("Unable to load color scheme named %1. Also tried %2.").arg(name, filename);
0159             return false;
0160         }
0161     }
0162 
0163     //If we reach here, the file should have been successfully opened
0164     QTextStream stream(&file);
0165 
0166     //first line is the star-color mode and star color intensity and dark palette
0167     QString line      = stream.readLine();
0168     QStringList modes = line.split(':');
0169 
0170     // Star Color Mode
0171     if (modes.count() > 0)
0172     {
0173         int newmode = modes[0].toInt(&ok);
0174         if (ok)
0175             setStarColorMode(newmode);
0176     }
0177 
0178     // Star Intensity
0179     if (modes.count() > 1)
0180     {
0181         int newintens = modes[1].toInt(&ok);
0182         if (ok)
0183             setStarColorIntensity(newintens);
0184     }
0185 
0186     // Dark Palette
0187 #if 0
0188     if (modes.count() > 2)
0189     {
0190         int newintens = modes[2].toInt(&ok);
0191         if (ok)
0192             setDarkPalette(newintens == 1);
0193     }
0194 #endif
0195 
0196     //More flexible method for reading in color values.  Any order is acceptable, and
0197     //missing entries are ignored.
0198     while (!stream.atEnd())
0199     {
0200         line = stream.readLine();
0201 
0202         if (line.count(':') ==
0203                 1) //the new color preset format contains a ":" in each line, followed by the name of the color
0204         {
0205             ++inew;
0206             if (iold)
0207                 return false; //we read at least one line without a colon...file is corrupted.
0208 
0209             //If this line has a valid Key, set the color.
0210             QString tkey  = line.mid(line.indexOf(':') + 1).trimmed();
0211             QString tname = line.left(line.indexOf(':') - 1);
0212 
0213             if (KeyName.contains(tkey))
0214             {
0215                 setColor(tkey, tname);
0216             }
0217             else //attempt to translate from old color ID
0218             {
0219                 QString k(line.mid(5).trimmed() + "Color");
0220                 if (KeyName.contains(k))
0221                 {
0222                     setColor(k, tname);
0223                 }
0224                 else
0225                 {
0226                     qWarning() << "Could not use the key \"" << tkey << "\" from the color scheme file \"" << filename
0227                                << "\".  I also tried \"" << k << "\".";
0228                 }
0229             }
0230         }
0231         else // no ':' seen in the line, so we must assume the old format
0232         {
0233             ++iold;
0234             if (inew)
0235                 return false; //a previous line had a colon, this line doesn't.  File is corrupted.
0236 
0237             //Assuming the old *.colors format.  Loop through the KeyName list,
0238             //and assign each color.  Note that order matters here, but only here
0239             //(so if you don't use the old format, then order doesn't ever matter)
0240             foreach (const QString &key, KeyName)
0241                 setColor(key, line.left(7));
0242         }
0243     }
0244 
0245     FileName = filename;
0246     return true;
0247 }
0248 
0249 bool ColorScheme::save(const QString &name)
0250 {
0251     QFile file;
0252 
0253     //Construct a file name from the scheme name.  Make lowercase, replace spaces with "-",
0254     //and append ".colors".
0255     QString filename = name.toLower().trimmed();
0256     if (!filename.isEmpty())
0257     {
0258         for (int i = 0; i < filename.length(); ++i)
0259             if (filename.at(i) == ' ')
0260                 filename.replace(i, 1, "-");
0261 
0262         filename = filename.append(".colors");
0263         //determine filename in local user KDE directory tree.
0264         file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(filename));
0265 
0266         //if (file.exists() || !file.open(QIODevice::ReadWrite | QIODevice::Append))
0267         if (!file.open(QIODevice::ReadWrite))
0268         {
0269             KSNotification::sorry(i18n("Local color scheme file could not be opened.\nScheme cannot be recorded."));
0270             return false;
0271         }
0272         else
0273         {
0274             QTextStream stream(&file);
0275             stream << StarColorMode << ":" << StarColorIntensity << ":" << DarkPalette << '\n';
0276 
0277             foreach (const QString &key, KeyName)
0278                 stream << Palette[key] << " :" << key << '\n';
0279             file.close();
0280         }
0281 
0282         //determine filename in local user KDE directory tree.
0283         file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("colors.dat"));
0284 
0285         if (!file.open(QIODevice::ReadWrite))
0286         {
0287             KSNotification::sorry(i18n("Local color scheme index file could not be opened.\nScheme cannot be recorded."));
0288         }
0289         else
0290         {
0291             bool found = false;
0292             QString schemeLine = name + ':' + filename;
0293 
0294             // Check if the scheme line is in the colors.dat file
0295             // If not, then we add it
0296             QTextStream stream(&file);
0297             while (stream.atEnd() == false)
0298             {
0299                 QString line = stream.readLine();
0300                 if (line == schemeLine)
0301                 {
0302                     found = true;
0303                     break;
0304                 }
0305             }
0306 
0307             if (found == false)
0308             {
0309                 stream << schemeLine << '\n';
0310                 file.close();
0311             }
0312         }
0313     }
0314     else
0315     {
0316         KSNotification::sorry("Invalid filename requested.\nScheme cannot be recorded.");
0317         return false;
0318     }
0319 
0320     FileName = filename;
0321     saveToConfig();
0322     return true;
0323 }
0324 
0325 void ColorScheme::loadFromConfig()
0326 {
0327     KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
0328 
0329     for (int i = 0; i < KeyName.size(); ++i)
0330         setColor(KeyName.at(i), cg.readEntry(KeyName.at(i).toUtf8().constData(), Default.at(i)));
0331 
0332     setStarColorModeIntensity(cg.readEntry("StarColorMode", 0), cg.readEntry("StarColorIntensity", 5));
0333     //setDarkPalette(cg.readEntry("DarkAppColors", false));
0334 
0335     FileName = cg.readEntry("ColorSchemeFile", "moonless-night.colors");
0336 }
0337 
0338 void ColorScheme::saveToConfig()
0339 {
0340     KConfigGroup cg = KSharedConfig::openConfig()->group("Colors");
0341     for (int i = 0; i < KeyName.size(); ++i)
0342     {
0343         QString c = colorNamed(KeyName.at(i)).name();
0344         cg.writeEntry(KeyName.at(i), c);
0345     }
0346 
0347     cg.writeEntry("StarColorMode", starColorMode());
0348     cg.writeEntry("StarColorIntensity", starColorIntensity());
0349     cg.writeEntry("ColorSchemeFile", FileName);
0350     cg.writeEntry("DarkAppColors", useDarkPalette());
0351 }
0352 
0353 void ColorScheme::setStarColorMode(int mode)
0354 {
0355     StarColorMode = mode;
0356     Options::setStarColorMode(mode);
0357 #ifndef KSTARS_LITE
0358     SkyQPainter::initStarImages();
0359 #endif
0360 }
0361 
0362 #if 0
0363 void ColorScheme::setDarkPalette(bool enable)
0364 {
0365     DarkPalette = enable ? 1 : 0;
0366     Options::setDarkAppColors(enable);
0367 #ifndef KSTARS_LITE
0368     SkyQPainter::initStarImages();
0369 #endif
0370 }
0371 #endif
0372 
0373 void ColorScheme::setStarColorIntensity(int intens)
0374 {
0375     StarColorIntensity = intens;
0376     Options::setStarColorIntensity(intens);
0377 #ifndef KSTARS_LITE
0378     SkyQPainter::initStarImages();
0379 #endif
0380 }
0381 
0382 void ColorScheme::setStarColorModeIntensity(int mode, int intens)
0383 {
0384     StarColorMode      = mode;
0385     StarColorIntensity = intens;
0386     Options::setStarColorMode(mode);
0387     Options::setStarColorIntensity(intens);
0388 #ifndef KSTARS_LITE
0389     SkyQPainter::initStarImages();
0390 #endif
0391 }