Skip to content

Commit 61b41c1

Browse files
committed
Added xml settings implementation.
1 parent 17f2ab9 commit 61b41c1

File tree

3 files changed

+372
-1
lines changed

3 files changed

+372
-1
lines changed

source/base/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ list(APPEND SOURCE_BASE
7272
version_constants.cc
7373
version_constants.h
7474
waitable_event.cc
75-
waitable_event.h)
75+
waitable_event.h
76+
xml_settings.cc
77+
xml_settings.h)
7678

7779
if (WIN32)
7880
list(APPEND SOURCE_BASE

source/base/xml_settings.cc

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2025 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#include "base/xml_settings.h"
20+
21+
#include <QDataStream>
22+
#include <QRect>
23+
#include <QXmlStreamReader>
24+
25+
namespace base {
26+
27+
namespace {
28+
29+
const QString kExtension = QStringLiteral("conf");
30+
const QString kSettingsElement = QStringLiteral("Settings");
31+
const QString kGroupElement = QStringLiteral("Group");
32+
const QString kValueElement = QStringLiteral("Value");
33+
const QString kNameAttribute = QStringLiteral("Name");
34+
const QString kTypeAttribute = QStringLiteral("Type");
35+
const QString kInvalidType = QStringLiteral("Invalid");
36+
const QString kByteArrayType = QStringLiteral("ByteArray");
37+
const QString kRectType = QStringLiteral("Rect");
38+
const QString kSizeType = QStringLiteral("Size");
39+
const QString kPointType = QStringLiteral("Point");
40+
const QString kVariantType = QStringLiteral("Variant");
41+
42+
QString variantToType(const QVariant& value)
43+
{
44+
QString result;
45+
46+
switch (value.type())
47+
{
48+
case QVariant::Invalid:
49+
result = kInvalidType;
50+
break;
51+
52+
case QVariant::Bool:
53+
case QVariant::Int:
54+
case QVariant::UInt:
55+
case QVariant::LongLong:
56+
case QVariant::ULongLong:
57+
case QVariant::Double:
58+
case QVariant::String:
59+
case QVariant::KeySequence:
60+
// Default type.
61+
break;
62+
63+
case QVariant::ByteArray:
64+
result = kByteArrayType;
65+
break;
66+
67+
case QVariant::Rect:
68+
result = kRectType;
69+
break;
70+
71+
case QVariant::Size:
72+
result = kSizeType;
73+
break;
74+
75+
case QVariant::Point:
76+
result = kPointType;
77+
break;
78+
79+
default:
80+
result = kVariantType;
81+
break;
82+
}
83+
84+
return result;
85+
}
86+
87+
QString variantToString(const QVariant& value)
88+
{
89+
QString result;
90+
91+
switch (value.type())
92+
{
93+
case QVariant::Invalid:
94+
break;
95+
96+
case QVariant::Bool:
97+
case QVariant::Int:
98+
case QVariant::UInt:
99+
case QVariant::LongLong:
100+
case QVariant::ULongLong:
101+
case QVariant::Double:
102+
case QVariant::String:
103+
case QVariant::KeySequence:
104+
result = value.toString();
105+
break;
106+
107+
case QVariant::ByteArray:
108+
result = value.toByteArray().toHex();
109+
break;
110+
111+
case QVariant::Rect:
112+
{
113+
QRect rect = value.toRect();
114+
result = QString::asprintf("%d %d %d %d",
115+
rect.left(), rect.top(), rect.width(), rect.height());
116+
}
117+
break;
118+
119+
case QVariant::Size:
120+
{
121+
QSize size = value.toSize();
122+
result = QString::asprintf("%d %d", size.width(), size.height());
123+
}
124+
break;
125+
126+
case QVariant::Point:
127+
{
128+
QPoint point = value.toPoint();
129+
result = QString::asprintf("%d %d", point.x(), point.y());
130+
}
131+
break;
132+
133+
default:
134+
{
135+
QByteArray buffer;
136+
137+
{
138+
QDataStream stream(&buffer, QIODevice::WriteOnly);
139+
stream.setVersion(QDataStream::Qt_5_15);
140+
stream << value;
141+
}
142+
143+
result = buffer.toHex();
144+
}
145+
break;
146+
}
147+
148+
return result;
149+
}
150+
151+
QVariant stringToVariant(const QString& value, const QString& type)
152+
{
153+
if (type == kByteArrayType)
154+
{
155+
return QVariant(QByteArray::fromHex(value.toUtf8()));
156+
}
157+
else if (type == kVariantType)
158+
{
159+
QByteArray buffer = QByteArray::fromHex(value.toUtf8());
160+
161+
QDataStream stream(&buffer, QIODevice::ReadOnly);
162+
stream.setVersion(QDataStream::Qt_5_15);
163+
164+
QVariant result;
165+
stream >> result;
166+
return result;
167+
}
168+
else if (type == kRectType)
169+
{
170+
QStringList list = value.split(QLatin1Char(' '), Qt::SkipEmptyParts);
171+
if (list.size() == 4)
172+
return QVariant(QRect(list[0].toInt(), list[1].toInt(), list[2].toInt(), list[3].toInt()));
173+
}
174+
else if (type == kSizeType)
175+
{
176+
QStringList list = value.split(QLatin1Char(' '), Qt::SkipEmptyParts);
177+
if (list.size() == 2)
178+
return QVariant(QSize(list[0].toInt(), list[1].toInt()));
179+
}
180+
else if (type == kPointType)
181+
{
182+
QStringList list = value.split(QLatin1Char(' '), Qt::SkipEmptyParts);
183+
if (list.size() == 2)
184+
return QVariant(QPoint(list[0].toInt(), list[1].toInt()));
185+
}
186+
else if (type == kInvalidType)
187+
{
188+
return QVariant();
189+
}
190+
191+
return QVariant(value);
192+
}
193+
194+
} // namespace
195+
196+
// static
197+
QSettings::Format XmlSettings::format()
198+
{
199+
static QSettings::Format xml_format =
200+
QSettings::registerFormat(kExtension,
201+
XmlSettings::readFunc,
202+
XmlSettings::writeFunc,
203+
Qt::CaseSensitive);
204+
205+
return xml_format;
206+
}
207+
208+
// static
209+
bool XmlSettings::readFunc(QIODevice& device, QSettings::SettingsMap& map)
210+
{
211+
QXmlStreamReader xml(&device);
212+
QStringList segments;
213+
QString type;
214+
215+
bool settings_found = false;
216+
217+
while (!xml.atEnd() && !xml.hasError())
218+
{
219+
switch (xml.readNext())
220+
{
221+
case QXmlStreamReader::StartElement:
222+
{
223+
if (xml.name() == kSettingsElement)
224+
{
225+
settings_found = true;
226+
continue;
227+
}
228+
229+
if (!settings_found)
230+
return false;
231+
232+
QString name = xml.attributes().value(kNameAttribute).toString();
233+
if (name.isEmpty())
234+
return false;
235+
236+
segments.append(name);
237+
type = xml.attributes().value(kTypeAttribute).toString();
238+
}
239+
break;
240+
241+
case QXmlStreamReader::EndElement:
242+
{
243+
if (!segments.isEmpty())
244+
segments.removeLast();
245+
}
246+
break;
247+
248+
case QXmlStreamReader::Characters:
249+
{
250+
if (xml.isWhitespace())
251+
continue;
252+
253+
QString key;
254+
255+
for (int i = 0; i < segments.size(); ++i)
256+
{
257+
if (i != 0)
258+
key += QLatin1Char('/');
259+
key += segments.at(i);
260+
}
261+
262+
if (key.isEmpty())
263+
return false;
264+
265+
map[key] = stringToVariant(xml.text().toString(), type);
266+
}
267+
break;
268+
269+
default:
270+
break;
271+
}
272+
}
273+
274+
return !xml.hasError();
275+
}
276+
277+
// static
278+
bool XmlSettings::writeFunc(QIODevice& device, const QSettings::SettingsMap& map)
279+
{
280+
QXmlStreamWriter xml(&device);
281+
282+
xml.setAutoFormatting(true);
283+
xml.writeStartDocument();
284+
xml.writeStartElement(kSettingsElement);
285+
286+
QStringList oldSegments;
287+
288+
for (auto it = map.cbegin(), it_end = map.cend(); it != it_end; ++it)
289+
{
290+
QStringList segments = it.key().split(QLatin1Char('/'), Qt::SkipEmptyParts);
291+
int count = 0;
292+
293+
while (count < oldSegments.size() && segments.at(count) == oldSegments.at(count))
294+
++count;
295+
296+
for (int i = oldSegments.size() - 1; i >= count; --i)
297+
xml.writeEndElement();
298+
299+
for (int i = count; i < segments.size(); ++i)
300+
{
301+
if (i == segments.size() - 1)
302+
xml.writeStartElement(kValueElement);
303+
else
304+
xml.writeStartElement(kGroupElement);
305+
306+
xml.writeAttribute(kNameAttribute, segments.at(i));
307+
}
308+
309+
QString type = variantToType(it.value());
310+
if (!type.isEmpty())
311+
xml.writeAttribute(kTypeAttribute, type);
312+
313+
xml.writeCharacters(variantToString(it.value()));
314+
315+
oldSegments.swap(segments);
316+
}
317+
318+
for (int i = 0; i < oldSegments.size(); ++i)
319+
xml.writeEndElement();
320+
321+
xml.writeEndElement();
322+
xml.writeEndDocument();
323+
324+
return !xml.hasError();
325+
}
326+
327+
} // namespace base

source/base/xml_settings.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2025 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#ifndef BASE_XML_SETTINGS_H
20+
#define BASE_XML_SETTINGS_H
21+
22+
#include <QSettings>
23+
24+
#include "base/macros_magic.h"
25+
26+
namespace base {
27+
28+
class XmlSettings
29+
{
30+
public:
31+
static QSettings::Format format();
32+
33+
static bool readFunc(QIODevice& device, QSettings::SettingsMap& map);
34+
static bool writeFunc(QIODevice& device, const QSettings::SettingsMap& map);
35+
36+
private:
37+
DISALLOW_COPY_AND_ASSIGN(XmlSettings);
38+
};
39+
40+
} // namespace base
41+
42+
#endif // BASE_XML_SETTINGS_H

0 commit comments

Comments
 (0)