1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2016 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "xml.h"
14 #include "property.h"
15 #include "scoreElement.h"
16
17 namespace Ms {
18
19 //---------------------------------------------------------
20 // Xml
21 //---------------------------------------------------------
22
XmlWriter(Score * s)23 XmlWriter::XmlWriter(Score* s)
24 {
25 _score = s;
26 setCodec("UTF-8");
27 }
28
XmlWriter(Score * s,QIODevice * device)29 XmlWriter::XmlWriter(Score* s, QIODevice* device)
30 : QTextStream(device)
31 {
32 _score = s;
33 setCodec("UTF-8");
34 }
35
36 //---------------------------------------------------------
37 // pTag
38 //---------------------------------------------------------
39
pTag(const char * name,PlaceText place)40 void XmlWriter::pTag(const char* name, PlaceText place)
41 {
42 const char* tags[] = {
43 "auto", "above", "below", "left"
44 };
45 tag(name, tags[int(place)]);
46 }
47
48 //---------------------------------------------------------
49 // putLevel
50 //---------------------------------------------------------
51
putLevel()52 void XmlWriter::putLevel()
53 {
54 int level = stack.size();
55 for (int i = 0; i < level * 2; ++i)
56 *this << ' ';
57 }
58
59 //---------------------------------------------------------
60 // header
61 //---------------------------------------------------------
62
header()63 void XmlWriter::header()
64 {
65 *this << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
66 }
67
68 //---------------------------------------------------------
69 // stag
70 // <mops attribute="value">
71 //---------------------------------------------------------
72
stag(const QString & s)73 void XmlWriter::stag(const QString& s)
74 {
75 putLevel();
76 *this << '<' << s << '>' << endl;
77 stack.append(s.split(' ')[0]);
78 }
79
80 //---------------------------------------------------------
81 // stag
82 // <mops attribute="value">
83 //---------------------------------------------------------
84
stag(const ScoreElement * se,const QString & attributes)85 void XmlWriter::stag(const ScoreElement* se, const QString& attributes)
86 {
87 stag(se->name(), se, attributes);
88 }
89
90 //---------------------------------------------------------
91 // stag
92 // <mops attribute="value">
93 //---------------------------------------------------------
94
stag(const QString & name,const ScoreElement * se,const QString & attributes)95 void XmlWriter::stag(const QString& name, const ScoreElement* se, const QString& attributes)
96 {
97 putLevel();
98 *this << '<' << name;
99 if (!attributes.isEmpty())
100 *this << ' ' << attributes;
101 *this << '>' << endl;
102 stack.append(name);
103
104 if (_recordElements)
105 _elements.emplace_back(se, name);
106 }
107
108 //---------------------------------------------------------
109 // etag
110 // </mops>
111 //---------------------------------------------------------
112
etag()113 void XmlWriter::etag()
114 {
115 putLevel();
116 *this << "</" << stack.takeLast() << '>' << endl;
117 }
118
119 //---------------------------------------------------------
120 // tagE
121 // <mops attribute="value"/>
122 //---------------------------------------------------------
123
tagE(const char * format,...)124 void XmlWriter::tagE(const char* format, ...)
125 {
126 va_list args;
127 va_start(args, format);
128 putLevel();
129 *this << '<';
130 char buffer[BS];
131 vsnprintf(buffer, BS, format, args);
132 *this << buffer;
133 va_end(args);
134 *this << "/>" << endl;
135 }
136
137 //---------------------------------------------------------
138 // tagE
139 //---------------------------------------------------------
140
tagE(const QString & s)141 void XmlWriter::tagE(const QString& s)
142 {
143 putLevel();
144 *this << '<' << s << "/>\n";
145 }
146
147 //---------------------------------------------------------
148 // ntag
149 // <mops> without newline
150 //---------------------------------------------------------
151
ntag(const char * name)152 void XmlWriter::ntag(const char* name)
153 {
154 putLevel();
155 *this << "<" << name << ">";
156 }
157
158 //---------------------------------------------------------
159 // netag
160 // </mops> without indentation
161 //---------------------------------------------------------
162
netag(const char * s)163 void XmlWriter::netag(const char* s)
164 {
165 *this << "</" << s << '>' << endl;
166 }
167
168 //---------------------------------------------------------
169 // tag
170 //---------------------------------------------------------
171
tag(Pid id,QVariant data,QVariant defaultData)172 void XmlWriter::tag(Pid id, QVariant data, QVariant defaultData)
173 {
174 if (data == defaultData)
175 return;
176 const char* name = propertyName(id);
177 if (name == 0)
178 return;
179 const QString writableVal(propertyToString(id, data, /* mscx */ true));
180 if (writableVal.isEmpty())
181 tag(name, data);
182 else
183 tag(name, QVariant(writableVal));
184 }
185
186 //---------------------------------------------------------
187 // tag
188 // <mops>value</mops>
189 //---------------------------------------------------------
190
tag(const char * name,QVariant data,QVariant defaultData)191 void XmlWriter::tag(const char* name, QVariant data, QVariant defaultData)
192 {
193 if (data != defaultData)
194 tag(QString(name), data);
195 }
196
tag(const QString & name,QVariant data)197 void XmlWriter::tag(const QString& name, QVariant data)
198 {
199 QString ename(name.split(' ')[0]);
200
201 putLevel();
202 switch(data.type()) {
203 case QVariant::Bool:
204 case QVariant::Char:
205 case QVariant::Int:
206 case QVariant::UInt:
207 *this << "<" << name << ">";
208 *this << data.toInt();
209 *this << "</" << ename << ">\n";
210 break;
211 case QVariant::LongLong:
212 *this << "<" << name << ">";
213 *this << data.toLongLong();
214 *this << "</" << ename << ">\n";
215 break;
216 case QVariant::Double:
217 *this << "<" << name << ">";
218 *this << data.value<double>();
219 *this << "</" << ename << ">\n";
220 break;
221 case QVariant::String:
222 *this << "<" << name << ">";
223 *this << xmlString(data.value<QString>());
224 *this << "</" << ename << ">\n";
225 break;
226 case QVariant::Color:
227 {
228 QColor color(data.value<QColor>());
229 *this << QString("<%1 r=\"%2\" g=\"%3\" b=\"%4\" a=\"%5\"/>\n")
230 .arg(name).arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha());
231 }
232 break;
233 case QVariant::Rect:
234 {
235 const QRect& r(data.value<QRect>());
236 *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
237 }
238 break;
239 case QVariant::RectF:
240 {
241 const QRectF& r(data.value<QRectF>());
242 *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
243 }
244 break;
245 case QVariant::PointF:
246 {
247 const QPointF& p(data.value<QPointF>());
248 *this << QString("<%1 x=\"%2\" y=\"%3\"/>\n").arg(name).arg(p.x()).arg(p.y());
249 }
250 break;
251 case QVariant::SizeF:
252 {
253 const QSizeF& p(data.value<QSizeF>());
254 *this << QString("<%1 w=\"%2\" h=\"%3\"/>\n").arg(name).arg(p.width()).arg(p.height());
255 }
256 break;
257 default: {
258 const char* type = data.typeName();
259 if (strcmp(type, "Ms::Spatium") == 0) {
260 *this << "<" << name << ">";
261 *this << data.value<Spatium>().val();
262 *this << "</" << ename << ">\n";
263 }
264 else if (strcmp(type, "Ms::Fraction") == 0) {
265 const Fraction& f = data.value<Fraction>();
266 *this << QString("<%1>%2/%3</%1>\n").arg(name).arg(f.numerator()).arg(f.denominator());
267 }
268 else if (strcmp(type, "Ms::Direction") == 0)
269 *this << QString("<%1>%2</%1>\n").arg(name, toString(data.value<Direction>()));
270 else if (strcmp(type, "Ms::Align") == 0) {
271 // TODO: remove from here? (handled in Ms::propertyWritableValue())
272 Align a = Align(data.toInt());
273 const char* h;
274 if (a & Align::HCENTER)
275 h = "center";
276 else if (a & Align::RIGHT)
277 h = "right";
278 else
279 h = "left";
280 const char* v;
281 if (a & Align::BOTTOM)
282 v = "bottom";
283 else if (a & Align::VCENTER)
284 v = "center";
285 else if (a & Align::BASELINE)
286 v = "baseline";
287 else
288 v = "top";
289 *this << QString("<%1>%2,%3</%1>\n").arg(name)
290 .arg(h, v);
291 }
292 else {
293 qFatal("XmlWriter::tag: unsupported type %d %s", data.type(), type);
294 }
295 }
296 break;
297 }
298 }
299
tag(const char * name,const QWidget * g)300 void XmlWriter::tag(const char* name, const QWidget* g)
301 {
302 tag(name, QRect(g->pos(), g->size()));
303 }
304
305 //---------------------------------------------------------
306 // comment
307 //---------------------------------------------------------
308
comment(const QString & text)309 void XmlWriter::comment(const QString& text)
310 {
311 putLevel();
312 *this << "<!-- " << text << " -->" << endl;
313 }
314
315 //---------------------------------------------------------
316 // xmlString
317 //---------------------------------------------------------
318
xmlString(ushort c)319 QString XmlWriter::xmlString(ushort c)
320 {
321 switch(c) {
322 case '<':
323 return QLatin1String("<");
324 case '>':
325 return QLatin1String(">");
326 case '&':
327 return QLatin1String("&");
328 case '\"':
329 return QLatin1String(""");
330 default:
331 // ignore invalid characters in xml 1.0
332 if ((c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D))
333 return QString();
334 return QString(QChar(c));
335 }
336 }
337
338 //---------------------------------------------------------
339 // xmlString
340 //---------------------------------------------------------
341
xmlString(const QString & s)342 QString XmlWriter::xmlString(const QString& s)
343 {
344 QString escaped;
345 escaped.reserve(s.size());
346 for (int i = 0; i < s.size(); ++i) {
347 ushort c = s.at(i).unicode();
348 escaped += xmlString(c);
349 }
350 return escaped;
351 }
352
353 //---------------------------------------------------------
354 // dump
355 //---------------------------------------------------------
356
dump(int len,const unsigned char * p)357 void XmlWriter::dump(int len, const unsigned char* p)
358 {
359 putLevel();
360 int col = 0;
361 setFieldWidth(5);
362 setNumberFlags(numberFlags() | QTextStream::ShowBase);
363 setIntegerBase(16);
364 for (int i = 0; i < len; ++i, ++col) {
365 if (col >= 16) {
366 setFieldWidth(0);
367 *this << endl;
368 col = 0;
369 putLevel();
370 setFieldWidth(5);
371 }
372 *this << (p[i] & 0xff);
373 }
374 if (col)
375 *this << endl << dec;
376 setFieldWidth(0);
377 setIntegerBase(10);
378 }
379
380 //---------------------------------------------------------
381 // writeXml
382 // string s is already escaped (& -> "&")
383 //---------------------------------------------------------
384
writeXml(const QString & name,QString s)385 void XmlWriter::writeXml(const QString& name, QString s)
386 {
387 QString ename(name.split(' ')[0]);
388 putLevel();
389 for (int i = 0; i < s.size(); ++i) {
390 ushort c = s.at(i).unicode();
391 if (c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D)
392 s[i] = '?';
393 }
394 *this << "<" << name << ">";
395 *this << s;
396 *this << "</" << ename << ">\n";
397 }
398
399 //---------------------------------------------------------
400 // assignLocalIndex
401 //---------------------------------------------------------
402
assignLocalIndex(const Location & mainElementLocation)403 int XmlWriter::assignLocalIndex(const Location& mainElementLocation)
404 {
405 return _linksIndexer.assignLocalIndex(mainElementLocation);
406 }
407
408 //---------------------------------------------------------
409 // canWrite
410 //---------------------------------------------------------
411
canWrite(const Element * e) const412 bool XmlWriter::canWrite(const Element* e) const
413 {
414 if (!_clipboardmode)
415 return true;
416 return _filter.canSelect(e);
417 }
418
419 //---------------------------------------------------------
420 // canWriteVoice
421 //---------------------------------------------------------
422
canWriteVoice(int track) const423 bool XmlWriter::canWriteVoice(int track) const
424 {
425 if (!_clipboardmode)
426 return true;
427 return _filter.canSelectVoice(track);
428 }
429
430 }
431
432
433