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