LP-311 Remove basic/advanced stabilization tab auto-switch (autotune/txpid lock issues)
[librepilot.git] / ground / gcs / src / libs / utils / xmlconfig.cpp
blob4a61369da308940c068808f375d5aacfcf038ced
1 /**
2 ******************************************************************************
4 * @file xmlconfig.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @see The GNU Public License (GPL) Version 3
8 * @brief Widget for Import/Export Plugin
9 * @addtogroup GCSPlugins GCS Plugins
10 * @{
11 * @addtogroup importexportplugin
12 * @{
14 *****************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 /* Nokia Corporation */
31 #include "xmlconfig.h"
33 #include <QtDebug>
34 #include <QStringList>
35 #include <QRegExp>
37 #include <QVariant>
38 #include <QRect>
39 #include <QSize>
40 #include <QPoint>
41 #include <QtCore/QUrl>
43 #define NUM_PREFIX "arr_"
45 QString XmlConfig::rootName = "gcs";
47 const QSettings::Format XmlConfig::XmlSettingsFormat =
48 QSettings::registerFormat("xml", XmlConfig::readXmlFile, XmlConfig::writeXmlFile);
51 bool XmlConfig::readXmlFile(QIODevice &device, QSettings::SettingsMap &map)
53 QDomDocument domDoc;
54 QDomElement root;
55 QString errorStr;
56 int errorLine;
57 int errorColumn;
59 if (!domDoc.setContent(&device, true, &errorStr, &errorLine,
60 &errorColumn)) {
61 QString err = QString(tr("GCS config")) +
62 tr("Parse error at line %1, column %2:\n%3")
63 .arg(errorLine)
64 .arg(errorColumn)
65 .arg(errorStr);
66 qFatal("%s", err.toLatin1().data());
67 return false;
69 root = domDoc.documentElement();
70 handleNode(&root, map);
72 return true;
75 void XmlConfig::handleNode(QDomElement *node, QSettings::SettingsMap &map, QString path)
77 if (!node) {
78 return;
80 // qDebug() << "XmlConfig::handleNode start";
82 QString nodeName = node->nodeName();
83 // For arrays, QT will use simple numbers as keys, which is not a valid element in XML.
84 // Therefore we prefixed these.
85 if (nodeName.startsWith(NUM_PREFIX)) {
86 nodeName.replace(NUM_PREFIX, "");
88 // Xml tags are restrictive with allowed characters,
89 // so we urlencode and replace % with __PCT__ on file
90 nodeName = nodeName.replace("__PCT__", "%");
91 nodeName = QUrl::fromPercentEncoding(nodeName.toLatin1());
93 if (nodeName == XmlConfig::rootName) {
95 } else if (path == "") {
96 path = nodeName;
97 } else {
98 path += "/" + nodeName;
101 // qDebug() << "Node: " << ": " << path << " Children: " << node->childNodes().length();
102 for (int i = 0; i < node->childNodes().length(); ++i) {
103 QDomNode child = node->childNodes().item(i);
104 if (child.isElement()) {
105 handleNode(static_cast<QDomElement *>(&child), map, path);
106 } else if (child.isText()) {
107 // qDebug() << "Key: " << path << " Value:" << node->text();
108 map.insert(path, stringToVariant(node->text()));
109 } else {
110 qDebug() << "Child not Element or text!" << child.nodeType();
113 // qDebug() << "XmlConfig::handleNode end";
116 bool XmlConfig::writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map)
118 QDomDocument outDocument;
120 // qDebug() << "writeXmlFile start";
121 outDocument.appendChild(outDocument.createElement(XmlConfig::rootName));
122 QMapIterator<QString, QVariant> iter(map);
123 while (iter.hasNext()) {
124 iter.next();
125 // qDebug() << "Entry: " << iter.key() << ": " << iter.value().toString() << endl;
126 QDomNode node = outDocument.firstChild();
127 foreach(QString elem, iter.key().split('/')) {
128 if (elem == "") {
129 continue;
131 // Xml tags are restrictive with allowed characters,
132 // so we urlencode and replace % with __PCT__ on file
133 elem = QString(QUrl::toPercentEncoding(elem));
134 elem = elem.replace("%", "__PCT__");
135 // For arrays, QT will use simple numbers as keys, which is not a valid element in XML.
136 // Therefore we prefixed these.
137 if (elem.startsWith(NUM_PREFIX)) {
138 qWarning() << "ERROR: Settings must not start with " << NUM_PREFIX
139 << " in: " + iter.key();
141 if (QRegExp("[0-9]").exactMatch(elem.left(1))) {
142 elem.prepend(NUM_PREFIX);
144 if (node.firstChildElement(elem).isNull()) {
145 node.appendChild(outDocument.createElement(elem));
147 node = node.firstChildElement(elem);
149 node.appendChild(outDocument.createTextNode(variantToString(iter.value())));
151 device.write(outDocument.toByteArray(2).constData());
152 // qDebug() << "Dokument:\n" << outDocument.toByteArray(2).constData();
153 // qDebug() << "writeXmlFile end";
154 return true;
158 QSettings::SettingsMap XmlConfig::settingsToMap(QSettings & qs)
160 qDebug() << "settingsToMap:---------------";
161 QSettings::SettingsMap map;
162 QStringList keys = qs.allKeys();
163 foreach(QString key, keys) {
164 QVariant val = qs.value(key);
166 qDebug() << key << val.toString();
167 map.insert(key, val);
169 qDebug() << "settingsToMap End --------";
170 return map;
173 QString XmlConfig::variantToString(const QVariant &v)
175 QString result;
177 switch (v.type()) {
178 case QVariant::Invalid:
179 result = QLatin1String("@Invalid()");
180 break;
182 case QVariant::ByteArray:
184 QByteArray a = v.toByteArray().toBase64();
185 result = QLatin1String("@ByteArray(");
186 result += QString::fromLatin1(a.constData(), a.size());
187 result += QLatin1Char(')');
188 break;
191 case QVariant::String:
192 case QVariant::LongLong:
193 case QVariant::ULongLong:
194 case QVariant::Int:
195 case QVariant::UInt:
196 case QVariant::Bool:
197 case QVariant::Double:
198 case QVariant::KeySequence:
199 case QVariant::Color:
201 result = v.toString();
202 if (result.startsWith(QLatin1Char('@'))) {
203 result.prepend(QLatin1Char('@'));
205 break;
207 #ifndef QT_NO_GEOM_VARIANT
208 case QVariant::Rect:
210 QRect r = qvariant_cast<QRect>(v);
211 result += QLatin1String("@Rect(");
212 result += QString::number(r.x());
213 result += QLatin1Char(' ');
214 result += QString::number(r.y());
215 result += QLatin1Char(' ');
216 result += QString::number(r.width());
217 result += QLatin1Char(' ');
218 result += QString::number(r.height());
219 result += QLatin1Char(')');
220 break;
222 case QVariant::Size:
224 QSize s = qvariant_cast<QSize>(v);
225 result += QLatin1String("@Size(");
226 result += QString::number(s.width());
227 result += QLatin1Char(' ');
228 result += QString::number(s.height());
229 result += QLatin1Char(')');
230 break;
232 case QVariant::Point:
234 QPoint p = qvariant_cast<QPoint>(v);
235 result += QLatin1String("@Point(");
236 result += QString::number(p.x());
237 result += QLatin1Char(' ');
238 result += QString::number(p.y());
239 result += QLatin1Char(')');
240 break;
242 #endif // !QT_NO_GEOM_VARIANT
244 default:
246 #ifndef QT_NO_DATASTREAM
247 QByteArray a;
249 QDataStream s(&a, QIODevice::WriteOnly);
250 s.setVersion(QDataStream::Qt_4_0);
251 s << v;
254 result = QLatin1String("@Variant(");
255 result += QString::fromLatin1(a.toBase64().constData());
256 result += QLatin1Char(')');
257 // These were being much too noisy!!
258 // qDebug() << "Variant Type: " << v.type();
259 // qDebug()<< "Variant: " << result;
260 #else
261 Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support");
262 #endif
263 break;
267 return result;
270 QVariant XmlConfig::stringToVariant(const QString &s)
272 if (s.startsWith(QLatin1Char('@'))) {
273 if (s.endsWith(QLatin1Char(')'))) {
274 if (s.startsWith(QLatin1String("@ByteArray("))) {
275 return QVariant(QByteArray::fromBase64(s.toLatin1().mid(11, s.size() - 12)));
276 } else if (s.startsWith(QLatin1String("@Variant("))) {
277 #ifndef QT_NO_DATASTREAM
278 QByteArray a(QByteArray::fromBase64(s.toLatin1().mid(9)));
279 QDataStream stream(&a, QIODevice::ReadOnly);
280 stream.setVersion(QDataStream::Qt_4_0);
281 QVariant result;
282 stream >> result;
283 return result;
285 #else
286 Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support");
287 #endif
288 #ifndef QT_NO_GEOM_VARIANT
289 } else if (s.startsWith(QLatin1String("@Rect("))) {
290 QStringList args = splitArgs(s, 5);
291 if (args.size() == 4) {
292 return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()));
294 } else if (s.startsWith(QLatin1String("@Size("))) {
295 QStringList args = splitArgs(s, 5);
296 if (args.size() == 2) {
297 return QVariant(QSize(args[0].toInt(), args[1].toInt()));
299 } else if (s.startsWith(QLatin1String("@Point("))) {
300 QStringList args = splitArgs(s, 6);
301 if (args.size() == 2) {
302 return QVariant(QPoint(args[0].toInt(), args[1].toInt()));
304 #endif
305 } else if (s == QLatin1String("@Invalid()")) {
306 return QVariant();
309 if (s.startsWith(QLatin1String("@@"))) {
310 return QVariant(s.mid(1));
314 return QVariant(s);
317 QStringList XmlConfig::splitArgs(const QString &s, int idx)
319 int l = s.length();
321 Q_ASSERT(l > 0);
322 Q_ASSERT(s.at(idx) == QLatin1Char('('));
323 Q_ASSERT(s.at(l - 1) == QLatin1Char(')'));
325 QStringList result;
326 QString item;
328 for (++idx; idx < l; ++idx) {
329 QChar c = s.at(idx);
330 if (c == QLatin1Char(')')) {
331 Q_ASSERT(idx == l - 1);
332 result.append(item);
333 } else if (c == QLatin1Char(' ')) {
334 result.append(item);
335 item.clear();
336 } else {
337 item.append(c);
341 return result;
345 * @}
346 * @}