1 #include "LaunchConfig.hxx"
2
3 #include <set>
4
5 #include <Main/options.hxx>
6 #include <simgear/misc/sg_path.hxx>
7
8 #include <QSettings>
9 #include <QDebug>
10 #include <QIODevice>
11 #include <QDataStream>
12 #include <QClipboard>
13 #include <QGuiApplication>
14
15 static bool static_enableDownloadDirUI = true;
16 static QSettings::Format static_binaryFormat = QSettings::InvalidFormat;
17
binaryReadFunc(QIODevice & device,QSettings::SettingsMap & map)18 static bool binaryReadFunc(QIODevice &device, QSettings::SettingsMap &map)
19 {
20 QDataStream ds(&device);
21 int count;
22 ds >> count;
23 for (int i=0; i < count; ++i) {
24 QString k;
25 QVariant v;
26 ds >> k >> v;
27 map.insert(k, v);
28 }
29
30 return true;
31 }
32
binaryWriteFunc(QIODevice & device,const QSettings::SettingsMap & map)33 static bool binaryWriteFunc(QIODevice &device, const QSettings::SettingsMap &map)
34 {
35 QDataStream ds(&device);
36
37 ds << map.size();
38 Q_FOREACH(QString key, map.keys()) {
39 ds << key << map.value(key);
40 }
41
42 return true;
43 }
44
LaunchConfig(QObject * parent)45 LaunchConfig::LaunchConfig(QObject* parent) :
46 QObject(parent)
47 {
48 if (static_binaryFormat == QSettings::InvalidFormat) {
49 static_binaryFormat = QSettings::registerFormat("fglaunch",
50 &binaryReadFunc,
51 &binaryWriteFunc);
52 }
53 }
54
~LaunchConfig()55 LaunchConfig::~LaunchConfig()
56 {
57
58 }
59
reset()60 void LaunchConfig::reset()
61 {
62 m_values.clear();
63 }
64
applyToOptions() const65 void LaunchConfig::applyToOptions() const
66 {
67 const auto extraArgs = extraArgNames();
68 flightgear::Options* options = flightgear::Options::sharedInstance();
69 std::for_each(m_values.begin(), m_values.end(),
70 [options, &extraArgs](const Arg& arg)
71 {
72 const auto name = arg.arg.toStdString();
73 if (arg.origin == Launcher) {
74 auto it = extraArgs.find(name);
75 if (it != extraArgs.end()) {
76 return;
77 }
78 }
79 options->addOption(name, arg.value.toStdString());
80 });
81 }
82
extraArgNames() const83 std::set<std::string> LaunchConfig::extraArgNames() const
84 {
85 // build a set of all the extra args we have defined
86 std::set<std::string> r;
87 for (auto arg : m_values) {
88 // don't override prop: arguments
89 if (arg.arg == "prop") continue;
90
91 // allow some multi-valued arguments
92 if (arg.arg == "fg-scenery")
93 {
94 continue;
95 }
96
97 if (arg.origin == ExtraArgs)
98 r.insert(arg.arg.toStdString());
99 }
100 return r;
101 }
102
setArg(QString name,QString value,Origin origin)103 void LaunchConfig::setArg(QString name, QString value, Origin origin)
104 {
105 m_values.push_back(Arg(name, value, origin));
106 }
107
setArg(const std::string & name,const std::string & value)108 void LaunchConfig::setArg(const std::string &name, const std::string &value)
109 {
110 setArg(QString::fromStdString(name), QString::fromStdString(value), Launcher);
111 }
112
setProperty(QString path,QVariant value,Origin origin)113 void LaunchConfig::setProperty(QString path, QVariant value, Origin origin)
114 {
115 m_values.push_back(Arg("prop", path + "=" + value.toString(), origin));
116 }
117
setEnableDisableOption(QString name,bool value)118 void LaunchConfig::setEnableDisableOption(QString name, bool value)
119 {
120 m_values.push_back(Arg((value ? "enable-" : "disable-") + name, "", Launcher));
121 }
122
htmlForCommandLine()123 QString LaunchConfig::htmlForCommandLine()
124 {
125 QString html;
126 string_list commandLineOpts = flightgear::Options::sharedInstance()->extractOptions();
127 if (!commandLineOpts.empty()) {
128 html += tr("<p>Options passed on the command line:</p>\n");
129 html += "<ul>\n";
130 for (auto opt : commandLineOpts) {
131 html += QString("<li>--") + QString::fromStdString(opt) + "</li>\n";
132 }
133 html += "</ul>\n";
134 }
135
136 reset();
137 collect();
138
139 const auto extraArgs = extraArgNames();
140
141 html += tr("<p>Options set in the launcher:</p>\n");
142 html += "<ul>\n";
143 for (auto arg : valuesFromLauncher()) {
144 auto it = extraArgs.find(arg.arg.toStdString());
145 html += "<li>";
146 bool strikeThrough = (it != extraArgs.end());
147 if (strikeThrough) {
148 html += "<i>";
149 }
150 if (arg.value.isEmpty()) {
151 html += QString("--") + arg.arg;
152 } else if (arg.arg == "prop") {
153 html += QString("--") + arg.arg + ":" + arg.value;
154 } else {
155 html += QString("--") + arg.arg + "=" + arg.value;
156 }
157 if (strikeThrough) {
158 html += tr(" (will be skipped due to being specified as an additional argument)") + "</i> ";
159 }
160 html += "</li>\n";
161 }
162 html += "</ul>\n";
163
164 html += tr("<p>Options set as additional arguments:</p>\n");
165 html += "<ul>\n";
166 for (auto arg : valuesFromExtraArgs()) {
167 if (arg.value.isEmpty()) {
168 html += QString("<li>--") + arg.arg + "</li>\n";
169 } else if (arg.arg == "prop") {
170 html += QString("<li>--") + arg.arg + ":" + arg.value + "</li>\n";
171 } else {
172 html += QString("<li>--") + arg.arg + "=" + arg.value + "</li>\n";
173 }
174 }
175 html += "</ul>\n";
176
177 return html;
178 }
179
formatArgForClipboard(const LaunchConfig::Arg & a)180 static QString formatArgForClipboard(const LaunchConfig::Arg& a)
181 {
182 if (a.value.isEmpty()) {
183 return "--" + a.arg + " ";
184 } else if (a.arg == "prop") {
185 return "--" + a.arg + ":" + a.value + " ";
186 } else {
187 return "--" + a.arg + "=" + a.value + " ";
188 }
189 }
190
copyCommandLine()191 void LaunchConfig::copyCommandLine()
192 {
193 QString r;
194
195 for (auto opt : flightgear::Options::sharedInstance()->extractOptions()) {
196 r += "--" + QString::fromStdString(opt) + " ";
197 }
198
199 reset();
200 collect();
201 const auto extraArgs = extraArgNames();
202
203 for (auto arg : valuesFromLauncher()) {
204 auto it = extraArgs.find(arg.arg.toStdString());
205 if (it != extraArgs.end()) {
206 continue; // skipped due to extra arg overriding
207 }
208
209 r += formatArgForClipboard(arg);
210 }
211
212 for (auto arg : valuesFromExtraArgs()) {
213 r += formatArgForClipboard(arg);
214 }
215
216 QClipboard *clipboard = QGuiApplication::clipboard();
217 clipboard->setText(r);
218 }
219
saveConfigToINI()220 bool LaunchConfig::saveConfigToINI()
221 {
222 // create settings using default type (INI) and path (inside FG_HOME),
223 // as setup in initQSettings()
224 m_loadSaveSettings.reset(new QSettings);
225 emit save();
226 m_loadSaveSettings->sync();
227 m_loadSaveSettings.reset();
228
229 return true;
230 }
231
loadConfigFromINI()232 bool LaunchConfig::loadConfigFromINI()
233 {
234 // create settings using default type (INI) and path (inside FG_HOME),
235 // as setup in initQSettings()
236 m_loadSaveSettings.reset(new QSettings);
237 emit restore();
238 emit postRestore();
239 m_loadSaveSettings.reset();
240 return true;
241 }
242
saveConfigToFile(QString path)243 bool LaunchConfig::saveConfigToFile(QString path)
244 {
245 m_loadSaveSettings.reset(new QSettings(path, static_binaryFormat));
246 emit save();
247 m_loadSaveSettings.reset();
248 return true;
249 }
250
loadConfigFromFile(QString path)251 bool LaunchConfig::loadConfigFromFile(QString path)
252 {
253 m_loadSaveSettings.reset(new QSettings(path, static_binaryFormat));
254 emit restore();
255 // some things have an ordering dependency, give them a chance to run
256 // after other settings have been restored (eg, location or aircraft)
257 emit postRestore();
258 m_loadSaveSettings.reset();
259 return true;
260 }
261
getValueForKey(QString group,QString key,QVariant defaultValue) const262 QVariant LaunchConfig::getValueForKey(QString group, QString key, QVariant defaultValue) const
263 {
264 if (!m_loadSaveSettings) {
265 // becuase we load settings on component completion, we need
266 // to create the default implementation (using the INI file)
267 // on demand
268 m_loadSaveSettings.reset(new QSettings);
269 }
270
271 m_loadSaveSettings->beginGroup(group);
272 auto v = m_loadSaveSettings->value(key, defaultValue);
273 bool convertedOk = v.convert(static_cast<int>(defaultValue.type()));
274 if (!convertedOk) {
275 qWarning() << "type forcing on loaded value failed:" << key << v << v.typeName() << defaultValue;
276 }
277 // qInfo() << Q_FUNC_INFO << key << "value" << v << v.typeName() << convertedOk;
278 return v;
279 }
280
setValueForKey(QString group,QString key,QVariant var)281 void LaunchConfig::setValueForKey(QString group, QString key, QVariant var)
282 {
283 Q_ASSERT(m_loadSaveSettings);
284 m_loadSaveSettings->beginGroup(group);
285 // qInfo() << "saving" << key << "with value" << var << var.typeName();
286 m_loadSaveSettings->setValue(key, var);
287 m_loadSaveSettings->endGroup();
288 }
289
defaultDownloadDir() const290 QString LaunchConfig::defaultDownloadDir() const
291 {
292 return QString::fromStdString(flightgear::defaultDownloadDir().utf8Str());
293 }
294
enableDownloadDirUI() const295 bool LaunchConfig::enableDownloadDirUI() const
296 {
297 return static_enableDownloadDirUI;
298 }
299
setEnableDownloadDirUI(bool enableDownloadDirUI)300 void LaunchConfig::setEnableDownloadDirUI(bool enableDownloadDirUI)
301 {
302 static_enableDownloadDirUI = enableDownloadDirUI;
303 }
304
values() const305 auto LaunchConfig::values() const -> std::vector<Arg>
306 {
307 return m_values;
308 }
309
valuesFromLauncher() const310 auto LaunchConfig::valuesFromLauncher() const -> std::vector<Arg>
311 {
312 std::vector<Arg> result;
313 std::copy_if(m_values.begin(), m_values.end(), std::back_inserter(result), [](const Arg& a)
314 { return a.origin == Launcher; });
315 return result;
316 }
317
valuesFromExtraArgs() const318 auto LaunchConfig::valuesFromExtraArgs() const -> std::vector<Arg>
319 {
320 std::vector<Arg> result;
321 std::copy_if(m_values.begin(), m_values.end(), std::back_inserter(result), [](const Arg& a)
322 { return a.origin == ExtraArgs; });
323 return result;
324 }
325