3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "serialization.hpp"
23 #include "yaml-cpp/yaml.h"
26 #include <QStringList>
27 #include <QKeySequence>
28 #include <boost/iostreams/concepts.hpp>
29 #include <boost/iostreams/stream.hpp>
32 namespace ScIDE
{ namespace Settings
{
34 typedef QSettings::SettingsMap::const_iterator SettingsIterator
;
36 struct IODeviceSource
: boost::iostreams::source
{
38 IODeviceSource ( QIODevice
*dev
) : mDev(dev
) {}
39 std::streamsize
read(char* s
, std::streamsize n
)
41 // Read up to n characters from the input
42 // sequence into the buffer s, returning
43 // the number of characters read, or -1
44 // to indicate end-of-sequence.
46 qint64 ret
= mDev
->read(s
, n
);
47 if( ret
== 0 ) ret
= -1;
54 static QVariant
parseTextFormat( const YAML::Node
& node
)
58 if(node
.Type() != NodeType::Map
) {
59 qWarning("YAML parsing: a node tagged 'textFormat' has wrong type (not a map)");
67 n
= node
.FindValue("color");
68 if(n
&& n
->Type() == NodeType::Scalar
) {
70 fm
.setForeground(QColor(val
.c_str()));
73 n
= node
.FindValue("background");
74 if(n
&& n
->Type() == NodeType::Scalar
) {
76 QColor
color(val
.c_str());
78 fm
.setBackground(color
);
81 n
= node
.FindValue("bold");
82 if(n
&& n
->Type() == NodeType::Scalar
) {
83 bool bold
= n
->to
<bool>();
84 if(bold
) fm
.setFontWeight(QFont::Bold
);
87 n
= node
.FindValue("italic");
88 if(n
&& n
->Type() == NodeType::Scalar
) {
89 bool italic
= n
->to
<bool>();
90 fm
.setFontItalic(italic
);
93 n
= node
.FindValue("underline");
94 if(n
&& n
->Type() == NodeType::Scalar
) {
95 bool underline
= n
->to
<bool>();
96 fm
.setFontUnderline(underline
);
99 return QVariant::fromValue
<QTextCharFormat
>(fm
);
102 static QVariant
parseScalar( const YAML::Node
& node
)
104 using namespace YAML
;
108 case NodeType::Scalar
:
112 return QVariant( QString::fromUtf8(val
.c_str()) );
115 case NodeType::Sequence
:
119 for(it
= node
.begin(); it
!= node
.end(); ++it
)
120 list
.append( parseScalar( *it
) );
121 return QVariant::fromValue
<QVariantList
>( list
);
128 for(it
= node
.begin(); it
!= node
.end(); ++it
)
132 QVariant value
= parseScalar( it
.second() );
133 map
.insert( QString(key
.c_str()), value
);
135 return QVariant::fromValue
<QVariantMap
>( map
);
142 qWarning("YAML parsing: unsupported node type.");
147 static void parseNode( const YAML::Node
&node
, const QString
&parentKey
, QSettings::SettingsMap
&map
)
149 using namespace YAML
;
151 static const std::string
textFormatTag("!textFormat");
152 static const std::string
qVariantListTag("!QVariantList");
153 static const std::string
qVariantMapTag("!QVariantMap");
155 Q_ASSERT(node
.Type() == NodeType::Map
);
158 for(it
= node
.begin(); it
!= node
.end(); ++it
) {
161 QString
childKey( parentKey
);
162 if (!childKey
.isEmpty()) childKey
+= "/";
163 childKey
+= key
.c_str();
165 const YAML::Node
& childNode
= it
.second();
166 const std::string
& childTag
= childNode
.Tag();
168 if (childTag
== textFormatTag
)
169 map
.insert( childKey
, parseTextFormat(childNode
) );
170 else if (childTag
== qVariantListTag
|| childTag
== qVariantMapTag
|| childNode
.Type() != NodeType::Map
)
171 map
.insert( childKey
, parseScalar( childNode
) );
172 else if (childNode
.Type() == NodeType::Map
)
173 parseNode( childNode
, childKey
, map
);
177 bool readSettings(QIODevice
&device
, QSettings::SettingsMap
&map
)
179 using namespace YAML
;
182 boost::iostreams::stream
<IODeviceSource
> in( &device
);
185 while(parser
.GetNextDocument(doc
)) {
186 if( doc
.Type() != NodeType::Map
) continue;
188 parseNode( doc
, key
, map
);
193 catch (std::exception
& e
) {
194 qWarning() << "Exception when parsing YAML config file:" << e
.what();
199 static void writeTextFormat( const QTextCharFormat
&fm
, YAML::Emitter
&out
)
201 out
<< YAML::LocalTag("textFormat");
202 out
<< YAML::BeginMap
;
204 if (fm
.hasProperty(QTextFormat::ForegroundBrush
)) {
205 out
<< YAML::Key
<< "color";
206 out
<< YAML::Value
<< fm
.foreground().color().name().toStdString();
209 if (fm
.hasProperty(QTextFormat::BackgroundBrush
)) {
210 out
<< YAML::Key
<< "background";
211 out
<< YAML::Value
<< fm
.background().color().name().toStdString();
214 if (fm
.hasProperty(QTextFormat::FontWeight
)) {
215 out
<< YAML::Key
<< "bold";
216 out
<< YAML::Value
<< (fm
.fontWeight() == QFont::Bold
);
219 if (fm
.hasProperty(QTextFormat::FontItalic
)) {
220 out
<< YAML::Key
<< "italic";
221 out
<< YAML::Value
<< fm
.fontItalic();
224 if (fm
.hasProperty(QTextFormat::TextUnderlineStyle
)) {
225 qDebug("saving underline");
226 out
<< YAML::Key
<< "underline";
227 out
<< YAML::Value
<< fm
.fontUnderline();
233 static void writeValue( const QVariant
&var
, YAML::Emitter
&out
)
236 case QVariant::Invalid
:
241 case QVariant::KeySequence
:
243 QKeySequence kseq
= var
.value
<QKeySequence
>();
245 out
<< kseq
.toString( QKeySequence::PortableText
).toUtf8().constData();
251 out
<< YAML::LocalTag("QVariantList") << YAML::BeginSeq
;
253 QVariantList list
= var
.value
<QVariantList
>();
254 foreach (const QVariant
& var
, list
)
255 writeValue( var
, out
);
263 out
<< YAML::LocalTag("QVariantMap") << YAML::BeginMap
;
265 QVariantMap map
= var
.value
<QVariantMap
>();
266 QVariantMap::iterator it
;
267 for (it
= map
.begin(); it
!= map
.end(); ++it
)
269 out
<< YAML::Key
<< it
.key().toStdString();
271 writeValue( it
.value(), out
);
278 case QVariant::UserType
:
280 int utype
= var
.userType();
282 if (utype
== qMetaTypeId
<QTextCharFormat
>())
284 writeTextFormat( var
.value
<QTextCharFormat
>(), out
);
288 out
<< var
.toString().toUtf8().constData();
294 out
<< var
.toString().toUtf8().constData();
299 static void writeGroup( const QString
&groupKey
, YAML::Emitter
&out
,
300 SettingsIterator
&it
, const QSettings::SettingsMap
&map
)
302 out
<< YAML::BeginMap
;
304 int groupKeyLen
= groupKey
.size();
306 while(it
!= map
.end())
308 QString
key( it
.key() );
310 if (!key
.startsWith(groupKey
)) break;
312 int i_separ
= key
.indexOf("/", groupKeyLen
);
314 // There is child nodes
315 key
.truncate( i_separ
+ 1 );
317 QString
yamlKey(key
);
318 yamlKey
.remove(0, groupKeyLen
);
321 out
<< YAML::Key
<< yamlKey
.toStdString();
324 writeGroup( key
, out
, it
, map
);
328 // There is no child nodes
329 key
.remove(0, groupKeyLen
);
331 out
<< YAML::Key
<< key
.toStdString();
334 writeValue( it
.value(), out
);
343 bool writeSettings(QIODevice
&device
, const QSettings::SettingsMap
&map
)
347 SettingsIterator it
= map
.begin();
348 writeGroup( "", out
, it
, map
);
349 device
.write(out
.c_str());
352 catch (std::exception
& e
) {
353 qWarning() << "Exception when writing YAML config file:" << e
.what();
358 QSettings::Format
serializationFormat() {
359 static QSettings::Format format
=
360 QSettings::registerFormat( "yaml", readSettings
, writeSettings
);
362 if( format
== QSettings::InvalidFormat
)
363 qWarning("Could not register settings format");
368 void printSettings (const QSettings
* settings
)
372 cout
<< "config filename: " << settings
->fileName().toStdString() << endl
;
373 QStringList keys
= settings
->allKeys();
374 cout
<< "num keys: " << keys
.count() << endl
;
375 Q_FOREACH( QString key
, keys
) {
376 QVariant var
= settings
->value(key
);
377 if (var
.type() == QVariant::Invalid
)
378 cout
<< key
.toStdString() << ": <null>" << endl
;
379 else if (var
.type() == QVariant::String
)
380 cout
<< key
.toStdString() << ": " << var
.toString().toStdString() << endl
;
382 cout
<< key
.toStdString() << ": <unknown value type>" << endl
;
386 }} // namespace ScIDE::Settings