Kstars

extensions.cpp
1#include "extensions.h"
2#include "auxiliary/kspaths.h"
3#include "kstars_debug.h"
4#include "version.h"
5
6#include <QDir>
7#include <QIcon>
8#include <QDebug>
9#include <QProcess>
10
11
12extensions::extensions(QObject *parent) : QObject{ parent }
13{
14 found = new QMap<QString, extDetails>;
15 extensionProcess = new QProcess(this);
16
17 connect(extensionProcess, &QProcess::started, this, [ = ]()
18 {
19 emit extensionStateChanged(Ekos::EXTENSION_STARTED);
20 });
21 connect(extensionProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [ = ]()
22 {
23 emit extensionStateChanged(Ekos::EXTENSION_STOPPED);
24 });
25 connect(extensionProcess, &QProcess::readyRead, this, [this]
26 {
27 emit extensionOutput(extensionProcess->readAll());
28 });
29}
30
31bool extensions::discover()
32{
33 bool sucess = false;
34
35 QDir dir = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/extensions");
36 // Makes directory if not existing, doesn't change anything if already exists
37 if (dir.mkpath("."))
38 {
39 m_Directory = dir.absolutePath();
40 QStringList filesExe = dir.entryList(QStringList(), QDir::Files | QDir::Executable);
41 QStringList filesConf = dir.entryList(QStringList() << "*.conf", QDir::Files | QDir::Readable);
42 QStringList filesIcons = dir.entryList(QStringList() << "*.jpg" << "*.bmp" << "*.gif" << "*.png" << "*.svg",
44 if (! filesExe.isEmpty())
45 {
46 qCDebug(KSTARS) << "Found extension(s): " << filesExe;
47
48 // Remove any executable without a .conf fileROCm
49 foreach (QString exe, filesExe)
50 {
51 if (!filesConf.contains(QString(exe).append(".conf")))
52 {
53 qCDebug(KSTARS) << QString(".conf file not found for extension %1").arg(exe);
54 filesExe.removeOne(exe);
55 }
56 }
57
58 if (! filesExe.isEmpty())
59 {
60
61 // Remove any .conf files that don't share filename with an executable
62 foreach (QString conf, filesConf)
63 {
64 if (!filesExe.contains(QString(conf).remove(".conf")))
65 {
66 qCDebug(KSTARS) << QString("Extraneous extension %1 file found without executable").arg(conf);
67 filesConf.removeOne(conf);
68 }
69 // Remove any .conf file that is not valid
70 if (!confValid(conf))
71 {
72 qCDebug(KSTARS) << QString(".conf file %1 is not valid").arg(conf);
73 filesConf.removeOne(conf);
74 }
75 }
76
77 //Check if any executable doesn't have a valid .conf
78 foreach (QString exe, filesExe)
79 {
80 if (!filesConf.contains(QString(exe).append(".conf")))
81 {
82 filesExe.removeOne(exe);
83 }
84 }
85
86 // Check if we have any executables with valid .conf files and build map
87 if (! filesExe.isEmpty() && (filesExe.count() == filesConf.count()))
88 {
89 foreach (QString exe, filesExe)
90 {
91 extDetails m_ext;
92
93 QString iconName = "";
94 foreach (QString name, filesIcons)
95 {
96 if (name.contains(exe))
97 {
98 iconName = name;
99 break;
100 }
101 }
102 QIcon icon;
103 if (iconName != "")
104 {
105 QString temp;
106 temp.append(m_Directory).append("/").append(iconName);
107 icon.addFile(temp);
108 }
109 else
110 {
111 icon = QIcon::fromTheme("plugins");
112 }
113
114 QString confFileName;
115 QString tooltip = "";
116 bool runDetached = false;
117 confFileName.append(m_Directory).append("/").append(exe).append(".conf");
118 QFile confFile(confFileName);
119 if (confFile.exists())
120 {
121 if (confFile.open(QIODevice::ReadOnly) && confFile.isReadable())
122 {
123 QTextStream confTS = QTextStream(&confFile);
124 while (!confTS.atEnd())
125 {
126 QString confLine = confTS.readLine();
127 if (confLine.contains("tooltip="))
128 {
129 tooltip = confLine.right(confLine.length() - (confLine.indexOf("=")) - 1);
130 }
131 else if (confLine.contains("runDetached=true"))
132 {
133 runDetached = true;
134 }
135 }
136 }
137 else qCDebug(KSTARS) << QString("Can't access .conf file %1").arg(exe).append(".conf");
138 }
139 else qCDebug(KSTARS) << QString(".conf file %1 disappeared").arg(exe).append(".conf");
140
141 m_ext.tooltip = tooltip;
142 m_ext.icon = icon;
143 m_ext.detached = runDetached;
144 found->insert(exe, m_ext);
145
146 sucess = true;
147 }
148 }
149 else qCDebug(KSTARS) << "No extensions found with valid .conf files";
150 }
151 else qCDebug(KSTARS) << "No extensions found with .conf files";
152 }
153 else qCDebug(KSTARS) << "No extensions found";
154 }
155 else qCDebug(KSTARS) << "Could not access extensions directory";
156
157 return sucess;
158}
159
160bool extensions::confValid(const QString &filePath)
161{
162 // Check that the passed extension .conf file contains a line starting
163 // minimum_kstars_version=xx.yy.zz
164 // and that the xx.yy.zz is no higher that the KSTARS_VERSION
165
166 bool valid = false;
167
168 QString confFileName;
169 confFileName.append(m_Directory).append("/").append(filePath);
170 QFile confFile(confFileName);
171 if (confFile.exists())
172 {
173 if (confFile.open(QIODevice::ReadOnly) && confFile.isReadable())
174 {
175 QTextStream confTS = QTextStream(&confFile);
176 while (!confTS.atEnd())
177 {
178 QString confLine = confTS.readLine();
179 if (confLine.contains("minimum_kstars_version="))
180 {
181 QString minVersion = confLine.right(confLine.length() - (confLine.indexOf("=")) - 1);
182 QStringList minVersionElements = minVersion.split(".");
183 if (minVersionElements.count() == 3)
184 {
185 QList <int> minVersionElementInts;
186 foreach (QString element, minVersionElements)
187 {
188 if (element.toInt() || element == "0")
189 {
190 minVersionElementInts.append(element.toInt());
191 }
192 else break;
193 }
194 if (minVersionElementInts.count() == minVersionElements.count())
195 {
196 QStringList KStarsVersionElements = QString(KSTARS_VERSION).split(".");
197 QList <int> KStarsVersionElementInts;
198 foreach (QString element, KStarsVersionElements)
199 {
200 if (element.toInt() || element == "0")
201 {
202 KStarsVersionElementInts.append(element.toInt());
203 }
204 }
205 if (minVersionElementInts.at(0) <= KStarsVersionElementInts.at(0))
206 {
207 if (KStarsVersionElementInts.at(0) > minVersionElementInts.at(0))
208 {
209 valid = true;
210 }
211 else if (minVersionElementInts.at(1) <= KStarsVersionElementInts.at(1))
212 {
213 if (KStarsVersionElementInts.at(1) > minVersionElementInts.at(1))
214 {
215 valid = true;
216 }
217 else if (minVersionElementInts.at(2) <= KStarsVersionElementInts.at(2))
218 {
219 valid = true;
220 }
221 }
222 }
223
224 if (!valid) qCDebug(KSTARS) << QString(".conf file %1 requires a minimum KStars version of %2").arg(filePath, minVersion);
225
226 }
227 else qCDebug(KSTARS) << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
228 }
229 else qCDebug(KSTARS) << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
230 }
231 else qCDebug(KSTARS) << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
232 }
233 }
234 else qCDebug(KSTARS) << QString("Can't access .conf file %1").arg(filePath);
235 }
236 else qCDebug(KSTARS) << QString(".conf file %1 disappeared").arg(filePath);
237
238 return valid;
239}
240
241QIcon extensions::getIcon(const QString &name)
242{
243 if (found->contains(name))
244 {
245 extDetails m_ext = found->value("name");
246 return m_ext.icon;
247 }
248 else return QIcon();
249}
250
251QString extensions::getTooltip(const QString &name)
252{
253 if (found->contains(name))
254 {
255 extDetails m_ext = found->value(name);
256 return m_ext.tooltip;
257 }
258 else return "";
259}
260
261void extensions::run(const QString &extension)
262{
263 QString processPath;
264 processPath.append(QString("%1%2%3").arg(m_Directory, "/", extension));
265 QStringList arguments;
266 extensionProcess->setWorkingDirectory(m_Directory);
267 extDetails m_ext = found->value(extension);
268 if (m_ext.detached)
269 {
270 extensionProcess->startDetached(processPath, arguments);
271 }
272 else
273 {
274 extensionProcess->setProcessChannelMode(QProcess::MergedChannels);
275 extensionProcess->start(processPath, arguments);
276 }
277 emit extensionStateChanged(Ekos::EXTENSION_START_REQUESTED);
278}
279
280void extensions::stop()
281{
282 emit extensionStateChanged(Ekos::EXTENSION_STOP_REQUESTED);
283}
284
285void extensions::kill()
286{
287 extensionProcess->kill();
288}
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardAction id)
KGuiItem remove()
void addFile(const QString &fileName, const QSize &size, Mode mode, State state)
QIcon fromTheme(const QString &name)
void readyRead()
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
bool isEmpty() const const
bool removeOne(const AT &t)
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void started()
QString & append(QChar ch)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString right(qsizetype n) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
bool atEnd() const const
QString readLine(qint64 maxlen)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:53:01 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.