Kstars

extensions.cpp
1#include "extensions.h"
2#include "auxiliary/kspaths.h"
3#include "version.h"
4
5#include <QDir>
6#include <QIcon>
7#include <QDebug>
8#include <QProcess>
9
10extensions::extensions(QObject *parent) : QObject{ parent } {
11 found = new QMap<QString, extDetails>;
12 extensionProcess = new QProcess(this);
13
14 connect(extensionProcess, &QProcess::started, this, [=]()
15 {
16 emit extensionStateChanged(Ekos::EXTENSION_STARTED);
17 });
18 connect(extensionProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [=]()
19 {
20 emit extensionStateChanged(Ekos::EXTENSION_STOPPED);
21 });
22 connect(extensionProcess, &QProcess::readyRead, this, [this] {
23 emit extensionOutput(extensionProcess->readAll());
24 });
25}
26
27bool extensions::discover()
28{
29 bool sucess = false;
30
31 QDir dir = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/extensions");
32 // Makes directory if not existing, doesn't change anything if already exists
33 if (dir.mkpath(".")) {
34 m_Directory = dir.absolutePath();
35 QStringList filesExe = dir.entryList(QStringList(), QDir::Files | QDir::Executable);
36 QStringList filesConf = dir.entryList(QStringList() << "*.conf", QDir::Files | QDir::Readable);
37 QStringList filesIcons = dir.entryList(QStringList() << "*.jpg" << "*.bmp" << "*.gif" << "*.png" << "*.svg",
39 if (! filesExe.isEmpty()) {
40 qDebug() << "Found extension(s): " << filesExe;
41
42 // Remove any executable without a .conf fileROCm
43 foreach (QString exe, filesExe) {
44 if (!filesConf.contains(QString(exe).append(".conf"))) {
45 qDebug() << QString(".conf file not found for extension %1").arg(exe);
46 filesExe.removeOne(exe);
47 }
48 }
49
50 if (! filesExe.isEmpty()) {
51
52 // Remove any .conf files that don't share filename with an executable
53 foreach (QString conf, filesConf) {
54 if (!filesExe.contains(QString(conf).remove(".conf"))) {
55 qDebug() << QString("Extraneous extension %1 file found without executable").arg(conf);
56 filesConf.removeOne(conf);
57 }
58 // Remove any .conf file that is not valid
59 if (!confValid(conf)) {
60 qDebug() << QString(".conf file %1 is not valid").arg(conf);
61 filesConf.removeOne(conf);
62 }
63 }
64
65 //Check if any executable doesn't have a valid .conf
66 foreach (QString exe, filesExe) {
67 if (!filesConf.contains(QString(exe).append(".conf"))) {
68 filesExe.removeOne(exe);
69 }
70 }
71
72 // Check if we have any executables with valid .conf files and build map
73 if (! filesExe.isEmpty() && (filesExe.count() == filesConf.count())) {
74 foreach (QString exe, filesExe) {
75 extDetails m_ext;
76
77 QString iconName = "";
78 foreach (QString name, filesIcons) {
79 if (name.contains(exe)) {
80 iconName = name;
81 break;
82 }
83 }
84 QIcon icon;
85 if (iconName != "") {
86 QString temp;
87 temp.append(m_Directory).append("/").append(iconName);
88 icon.addFile(temp);
89 } else {
90 icon = QIcon::fromTheme("plugins");
91 }
92
93 QString confFileName;
94 QString tooltip = "";
95 bool runDetached = false;
96 confFileName.append(m_Directory).append("/").append(exe).append(".conf");
97 QFile confFile(confFileName);
98 if (confFile.exists()) {
99 if (confFile.open(QIODevice::ReadOnly) && confFile.isReadable()) {
100 QTextStream confTS = QTextStream(&confFile);
101 while (!confTS.atEnd()) {
102 QString confLine = confTS.readLine();
103 if (confLine.contains("tooltip=")) {
104 tooltip = confLine.right(confLine.length() - (confLine.indexOf("=")) - 1);
105 } else if (confLine.contains("runDetached=true")) {
106 runDetached = true;
107 }
108 }
109 } else qDebug() << QString("Can't access .conf file %1").arg(exe).append(".conf");
110 } else qDebug() << QString(".conf file %1 disappeared").arg(exe).append(".conf");
111
112 m_ext.tooltip = tooltip;
113 m_ext.icon = icon;
114 m_ext.detached = runDetached;
115 found->insert(exe, m_ext);
116
117 sucess = true;
118 }
119 } else qDebug() << "No extensions found with valid .conf files";
120 } else qDebug() << "No extensions found with .conf files";
121 } else qDebug() << "No extensions found";
122 } else qDebug() << "Could not access extensions directory";
123
124 return sucess;
125}
126
127bool extensions::confValid(const QString &filePath)
128{
129 // Check that the passed extension .conf file contains a line starting
130 // minimum_kstars_version=xx.yy.zz
131 // and that the xx.yy.zz is no higher that the KSTARS_VERSION
132
133 bool valid = false;
134
135 QString confFileName;
136 confFileName.append(m_Directory).append("/").append(filePath);
137 QFile confFile(confFileName);
138 if (confFile.exists()) {
139 if (confFile.open(QIODevice::ReadOnly) && confFile.isReadable()) {
140 QTextStream confTS = QTextStream(&confFile);
141 while (!confTS.atEnd()) {
142 QString confLine = confTS.readLine();
143 if (confLine.contains("minimum_kstars_version=")) {
144 QString minVersion = confLine.right(confLine.length() - (confLine.indexOf("=")) - 1);
145 QStringList minVersionElements = minVersion.split(".");
146 if (minVersionElements.count() == 3) {
147 QList <int> minVersionElementInts;
148 foreach (QString element, minVersionElements) {
149 if (element.toInt() || element == "0") {
150 minVersionElementInts.append(element.toInt());
151 } else break;
152 }
153 if (minVersionElementInts.count() == minVersionElements.count()) {
154 QStringList KStarsVersionElements = QString(KSTARS_VERSION).split(".");
155 QList <int> KStarsVersionElementInts;
156 foreach (QString element, KStarsVersionElements) {
157 if (element.toInt() || element == "0") {
158 KStarsVersionElementInts.append(element.toInt());
159 }
160 }
161 if (minVersionElementInts.at(0) <= KStarsVersionElementInts.at(0)) {
162 if (KStarsVersionElementInts.at(0) > minVersionElementInts.at(0)) {
163 valid = true;
164 } else if (minVersionElementInts.at(1) <= KStarsVersionElementInts.at(1)) {
165 if (KStarsVersionElementInts.at(1) > minVersionElementInts.at(1)) {
166 valid = true;
167 } else if (minVersionElementInts.at(2) <= KStarsVersionElementInts.at(2)) {
168 valid = true;
169 }
170 }
171 }
172
173 if (!valid) qDebug() << QString(".conf file %1 requires a minimum KStars version of %2").arg(filePath, minVersion);
174
175 } else qDebug() << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
176 } else qDebug() << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
177 } else qDebug() << QString(".conf file %1 does not contain a valid minimum_kstars_version string").append(filePath);
178 }
179 } else qDebug() << QString("Can't access .conf file %1").arg(filePath);
180 } else qDebug() << QString(".conf file %1 disappeared").arg(filePath);
181
182 return valid;
183}
184
185QIcon extensions::getIcon(const QString &name)
186{
187 if (found->contains(name)) {
188 extDetails m_ext = found->value("name");
189 return m_ext.icon;
190 } else return QIcon();
191}
192
193QString extensions::getTooltip(const QString &name)
194{
195 if (found->contains(name)) {
196 extDetails m_ext = found->value(name);
197 return m_ext.tooltip;
198 } else return "";
199}
200
201void extensions::run(const QString &extension)
202{
203 QString processPath;
204 processPath.append(QString("%1%2%3").arg(m_Directory, "/", extension));
205 QStringList arguments;
206 extensionProcess->setWorkingDirectory(m_Directory);
207 extDetails m_ext = found->value(extension);
208 if (m_ext.detached) {
209 extensionProcess->startDetached(processPath, arguments);
210 } else {
212 extensionProcess->start(processPath, arguments);
213 }
214 emit extensionStateChanged(Ekos::EXTENSION_START_REQUESTED);
215}
216
217void extensions::stop()
218{
219 emit extensionStateChanged(Ekos::EXTENSION_STOP_REQUESTED);
220}
221
222void extensions::kill()
223{
224 extensionProcess->kill();
225}
QString name(GameStandardAction id)
KIOCORE_EXPORT QString dir(const QString &fileClass)
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)
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void kill()
void setProcessChannelMode(ProcessChannelMode mode)
void setWorkingDirectory(const QString &dir)
void start(OpenMode mode)
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
void started()
QString & append(QChar ch)
QString arg(Args &&... args) const const
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-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.