MuseScore/libmscore/scorefile.cpp

1389 lines
50 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
// $Id:$
//
// Copyright (C) 2011 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENSE.GPL
//=============================================================================
2012-07-16 15:49:24 +02:00
#include "config.h"
2012-05-26 14:26:10 +02:00
#include "score.h"
#include "xml.h"
#include "element.h"
#include "measure.h"
#include "segment.h"
#include "slur.h"
#include "chordrest.h"
#include "tuplet.h"
#include "beam.h"
#include "revisions.h"
#include "page.h"
#include "part.h"
#include "staff.h"
#include "keysig.h"
#include "clef.h"
#include "text.h"
#include "ottava.h"
#include "volta.h"
#include "excerpt.h"
#include "mscore.h"
#include "stafftype.h"
2012-07-06 17:42:20 +02:00
#ifdef OMR
2012-05-26 14:26:10 +02:00
#include "omr/omr.h"
#include "omr/omrpage.h"
2012-07-06 17:42:20 +02:00
#endif
2012-05-26 14:26:10 +02:00
#include "sig.h"
#include "undo.h"
#include "imageStore.h"
#include "audio.h"
#include "libmscore/qzipreader_p.h"
#include "libmscore/qzipwriter_p.h"
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Score::write(Xml& xml, bool selectionOnly)
{
spanner.clear();
xml.stag("Score");
2012-07-06 17:42:20 +02:00
#ifdef OMR
2012-05-26 14:26:10 +02:00
if (_omr && xml.writeOmr)
_omr->write(xml);
2012-07-06 17:42:20 +02:00
#endif
2012-05-26 14:26:10 +02:00
if (_showOmr && xml.writeOmr)
xml.tag("showOmr", _showOmr);
if (_audio && xml.writeOmr) {
xml.tag("playMode", int(_playMode));
_audio->write(xml);
}
for (int i = 0; i < 32; ++i) {
if (!_layerTags[i].isEmpty()) {
xml.tag(QString("LayerTag id=\"%1\" tag=\"%2\"").arg(i).arg(_layerTags[i]),
_layerTagComments[i]);
}
}
int n = _layer.size();
for (int i = 1; i < n; ++i) { // dont save default variant
const Layer& l = _layer[i];
xml.tagE(QString("Layer name=\"%1\" mask=\"%2\"").arg(l.name).arg(l.tags));
}
xml.tag("currentLayer", _currentLayer);
if (!_testMode)
_syntiState.write(xml);
if (pageNumberOffset())
xml.tag("page-offset", pageNumberOffset());
xml.tag("Division", MScore::division);
xml.curTrack = -1;
_style.save(xml, true); // save only differences to buildin style
if (!parentScore()) {
int idx = 0;
foreach(StaffType* st, _staffTypes) {
if ((idx >= STAFF_TYPES) || !st->isEqual(*::staffTypes[idx]))
st->write(xml, idx);
++idx;
}
}
xml.tag("showInvisible", _showInvisible);
xml.tag("showUnprintable", _showUnprintable);
xml.tag("showFrames", _showFrames);
xml.tag("showMargins", _showPageborders);
QMapIterator<QString, QString> i(_metaTags);
while (i.hasNext()) {
i.next();
2012-08-09 12:12:43 +02:00
if (!_testMode || i.key() != "platform")
2012-05-26 14:26:10 +02:00
xml.tag(QString("metaTag name=\"%1\"").arg(i.key()), i.value());
}
foreach(KeySig* ks, customKeysigs)
ks->write(xml);
if (!selectionOnly) {
xml.stag("PageList");
foreach(Page* page, _pages)
page->write(xml);
xml.etag();
}
foreach(const Part* part, _parts)
part->write(xml);
xml.curTrack = 0;
int staffStart;
int staffEnd;
MeasureBase* measureStart;
MeasureBase* measureEnd;
if (selectionOnly) {
staffStart = _selection.staffStart();
staffEnd = _selection.staffEnd();
measureStart = _selection.startSegment()->measure();
// include title frames:
while (measureStart->prev() && !measureStart->prev()->sectionBreak())
measureStart = measureStart->prev();
measureEnd = _selection.endSegment()->measure()->next();
}
else {
staffStart = 0;
staffEnd = nstaves();
measureStart = first();
measureEnd = 0;
}
xml.trackDiff = -staffStart * VOICES;
for (int staffIdx = staffStart; staffIdx < staffEnd; ++staffIdx) {
xml.stag(QString("Staff id=\"%1\"").arg(staffIdx + 1));
xml.curTick = measureStart->tick();
xml.tickDiff = xml.curTick;
xml.curTrack = staffIdx * VOICES;
for (MeasureBase* m = measureStart; m != measureEnd; m = m->next()) {
2012-09-13 18:01:34 +02:00
if (m->type() == Element::MEASURE || staffIdx == 0)
2012-05-26 14:26:10 +02:00
m->write(xml, staffIdx, staffIdx == staffStart);
2012-09-13 18:01:34 +02:00
if (m->type() == Element::MEASURE)
2012-05-26 14:26:10 +02:00
xml.curTick = m->tick() + m->ticks();
}
xml.etag();
}
xml.curTrack = -1;
if (!selectionOnly) {
foreach(Excerpt* excerpt, _excerpts)
excerpt->score()->write(xml, false); // recursion
}
if (parentScore())
xml.tag("name", name());
xml.etag();
}
//---------------------------------------------------------
// readStaff
//---------------------------------------------------------
void Score::readStaff(const QDomElement& de)
{
MeasureBase* mb = first();
int staff = de.attribute("id", "1").toInt() - 1;
curTick = 0;
curTrack = staff * VOICES;
for (QDomElement e = de.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
const QString& tag(e.tagName());
if (tag == "Measure") {
Measure* measure = 0;
if (staff == 0) {
measure = new Measure(this);
measure->setTick(curTick);
add(measure);
if (_mscVersion < 115) {
const SigEvent& ev = sigmap()->timesig(measure->tick());
measure->setLen(ev.timesig());
measure->setTimesig(ev.nominal());
}
else {
//
// inherit timesig from previous measure
//
Measure* m = measure->prevMeasure();
Fraction f(m ? m->timesig() : Fraction(4,4));
measure->setLen(f);
measure->setTimesig(f);
}
}
else {
while (mb) {
2012-09-13 18:01:34 +02:00
if (mb->type() != Element::MEASURE) {
2012-05-26 14:26:10 +02:00
mb = mb->next();
}
else {
measure = (Measure*)mb;
mb = mb->next();
break;
}
}
if (measure == 0) {
qDebug("Score::readStaff(): missing measure!\n");
measure = new Measure(this);
measure->setTick(curTick);
add(measure);
}
}
measure->read(e, staff);
curTick = measure->tick() + measure->ticks();
}
else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
MeasureBase* mb = static_cast<MeasureBase*>(Element::name2Element(tag, this));
mb->read(e);
mb->setTick(curTick);
add(mb);
}
else
domError(e);
}
}
//---------------------------------------------------------
// saveFile
/// If file has generated name, create a modal file save dialog
/// and ask filename.
/// Rename old file to backup file (.xxxx.msc?,).
/// Default is to save score in .mscz format,
/// Return true if OK and false on error.
//---------------------------------------------------------
bool Score::saveFile()
{
QString suffix = info.suffix();
if ((suffix != "mscx") && (suffix != "mscz")) {
QString s = info.filePath();
if (!suffix.isEmpty())
s = s.left(s.size() - suffix.size());
else
s += ".";
if (suffix == "msc")
suffix = "mscx"; // silently change to mscx
else
suffix = "mscz";
s += suffix;
info.setFile(s);
}
if (info.exists() && !info.isWritable()) {
QString s = QT_TRANSLATE_NOOP("file", "The following file is locked: \n%1 \n\nTry saving to a different location.");
MScore::lastError = s.arg(info.filePath());
return false;
}
// if file was already saved in this session
// save but don't overwrite backup again
if (saved()) {
try {
if (suffix == "msc" || suffix == "mscx")
saveFile(info);
else
saveCompressedFile(info, false);
}
catch (QString s) {
MScore::lastError = s;
return false;
}
undo()->setClean();
setDirty(false);
return true;
}
//
// step 1
// save into temporary file to prevent partially overwriting
// the original file in case of "disc full"
//
QString tempName = info.filePath() + QString(".temp");
QFile temp(tempName);
if (!temp.open(QIODevice::WriteOnly)) {
MScore::lastError = QT_TRANSLATE_NOOP("file", "Open Temp File\n")
+ tempName + QT_TRANSLATE_NOOP("file", "\nfailed: ") + QString(strerror(errno));
return false;
}
try {
if (suffix == "msc" || suffix == "mscx")
saveFile(&temp, false);
else
saveCompressedFile(&temp, info, false);
}
catch (QString s) {
MScore::lastError = s;
return false;
}
if (temp.error() != QFile::NoError) {
MScore::lastError = QT_TRANSLATE_NOOP("file",
"MuseScore: Save File failed: ") + temp.errorString();
temp.close();
return false;
}
temp.close();
//
// step 2
// remove old backup file if exists
//
QDir dir(info.path());
QString backupName = QString(".") + info.fileName() + QString(",");
if (dir.exists(backupName)) {
if (!dir.remove(backupName)) {
// QMessageBox::critical(mscore, tr("MuseScore: Save File"),
// tr("removing old backup file ") + backupName + tr(" failed"));
}
}
//
// step 3
// rename old file into backup
//
QString name(info.filePath());
if (dir.exists(name)) {
if (!dir.rename(name, backupName)) {
// QMessageBox::critical(mscore, tr("MuseScore: Save File"),
// tr("renaming old file <")
// + name + tr("> to backup <") + backupName + tr("> failed"));
}
}
//
// step 4
// rename temp name into file name
//
if (!QFile::rename(tempName, name)) {
MScore::lastError = QT_TRANSLATE_NOOP("file", "renaming temp. file <")
+ tempName + QT_TRANSLATE_NOOP("file", "> to <") + name
+ QT_TRANSLATE_NOOP("file", "> failed:\n")
+ QString(strerror(errno));
return false;
}
// make file readable by all
QFile::setPermissions(name, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser
| QFile::ReadGroup | QFile::ReadOther);
undo()->setClean();
setDirty(false);
setSaved(true);
return true;
}
//---------------------------------------------------------
// saveCompressedFile
//---------------------------------------------------------
void Score::saveCompressedFile(QFileInfo& info, bool onlySelection)
{
if (info.suffix().isEmpty())
info.setFile(info.filePath() + ".mscz");
QFile fp(info.filePath());
if (!fp.open(QIODevice::WriteOnly)) {
QString s = QString("Open File\n") + info.filePath() + QString("\nfailed: ")
+ QString(strerror(errno));
throw(s);
}
saveCompressedFile(&fp, info, onlySelection);
fp.close();
}
//---------------------------------------------------------
// saveCompressedFile
// file is already opened
//---------------------------------------------------------
void Score::saveCompressedFile(QIODevice* f, QFileInfo& info, bool onlySelection)
{
QZipWriter uz(f);
2012-05-26 14:26:10 +02:00
QString fn = info.completeBaseName() + ".mscx";
QBuffer cbuf;
cbuf.open(QIODevice::ReadWrite);
Xml xml(&cbuf);
xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
xml.stag("container");
xml.stag("rootfiles");
xml.stag(QString("rootfile full-path=\"%1\"").arg(Xml::xmlString(fn)));
xml.etag();
foreach(ImageStoreItem* ip, imageStore) {
if (!ip->isUsed(this))
continue;
QString path = QString("Pictures/") + ip->hashName();
xml.tag("file", path);
}
xml.etag();
xml.etag();
cbuf.seek(0);
uz.addDirectory("META-INF");
uz.addFile("META-INF/container.xml", cbuf.data());
2012-05-26 14:26:10 +02:00
// save images
uz.addDirectory("Pictures");
2012-05-26 14:26:10 +02:00
foreach(ImageStoreItem* ip, imageStore) {
if (!ip->isUsed(this))
continue;
QString path = QString("Pictures/") + ip->hashName();
2012-06-23 11:42:40 +02:00
uz.addFile(path, ip->buffer());
2012-05-26 14:26:10 +02:00
}
#ifdef OMR
//
// save OMR page images
//
if (_omr) {
int n = _omr->numPages();
for (int i = 0; i < n; ++i) {
QString path = QString("OmrPages/page%1.png").arg(i+1);
QBuffer cbuf;
OmrPage* page = _omr->page(i);
QImage image = page->image();
if (!image.save(&cbuf, "PNG"))
throw(QString("cannot create image"));
uz.addFile(path, cbuf.data());
2012-05-26 14:26:10 +02:00
cbuf.close();
}
}
#endif
//
// save audio
//
2012-06-23 11:42:40 +02:00
if (_audio)
uz.addFile("audio.ogg", _audio->data());
2012-05-26 14:26:10 +02:00
QBuffer dbuf;
dbuf.open(QIODevice::ReadWrite);
saveFile(&dbuf, true, onlySelection);
dbuf.seek(0);
uz.addFile(fn, dbuf.data());
uz.close();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// saveFile
// return true on success
//---------------------------------------------------------
bool Score::saveFile(QFileInfo& info)
{
if (info.suffix().isEmpty())
info.setFile(info.filePath() + ".mscx");
QFile fp(info.filePath());
if (!fp.open(QIODevice::WriteOnly)) {
QString s = QString("Open File\n") + info.filePath() + QString("\nfailed: ")
+ QString(strerror(errno));
return false;
}
saveFile(&fp, false);
fp.close();
return true;
}
//---------------------------------------------------------
// loadStyle
//---------------------------------------------------------
bool Score::loadStyle(const QString& fn)
{
QFile f(fn);
if (f.open(QIODevice::ReadOnly)) {
MStyle st = _style;
if (st.load(&f)) {
_undo->push(new ChangeStyle(this, st));
return true;
}
}
MScore::lastError = strerror(errno);
return false;
}
//---------------------------------------------------------
// saveStyle
//---------------------------------------------------------
bool Score::saveStyle(const QString& name)
{
QString ext(".mss");
QFileInfo info(name);
if (info.suffix().isEmpty())
info.setFile(info.filePath() + ext);
QFile f(info.filePath());
if (!f.open(QIODevice::WriteOnly)) {
MScore::lastError = QT_TRANSLATE_NOOP("file", "Open Style File\n")
+ f.fileName() + QT_TRANSLATE_NOOP("file", "\nfailed: ")
+ QString(strerror(errno));
return false;
}
Xml xml(&f);
xml.header();
xml.stag("museScore version=\"" MSC_VERSION "\"");
_style.save(xml, false); // save complete style
xml.etag();
if (f.error() != QFile::NoError) {
MScore::lastError = QT_TRANSLATE_NOOP("file", "Write Style failed: ")
+ f.errorString();
return false;
}
return true;
}
//---------------------------------------------------------
// saveFile
// return true on success
//---------------------------------------------------------
extern QString revision;
void Score::saveFile(QIODevice* f, bool msczFormat, bool onlySelection)
{
Xml xml(f);
xml.writeOmr = msczFormat;
xml.header();
xml.stag("museScore version=\"" MSC_VERSION "\"");
if (!_testMode) {
xml.tag("programVersion", VERSION);
xml.tag("programRevision", revision);
}
write(xml, onlySelection);
xml.etag();
if (!parentScore())
_revisions->write(xml);
if(!onlySelection) {
//update version values for i.e. plugin access
_mscoreVersion = VERSION;
_mscoreRevision = revision.toInt();
_mscVersion = MSCVERSION;
}
}
//---------------------------------------------------------
// loadCompressedMsc
// return false on error
//---------------------------------------------------------
2012-09-29 16:46:45 +02:00
Score::FileError Score::loadCompressedMsc(QString name, bool ignoreVersionError)
2012-05-26 14:26:10 +02:00
{
QZipReader uz(name);
2012-06-23 11:42:40 +02:00
if (!uz.exists()) {
qDebug("loadCompressedMsc: <%s> not found\n", qPrintable(name));
MScore::lastError = QT_TRANSLATE_NOOP("file", "file not found");
2012-09-29 16:46:45 +02:00
return FILE_NOT_FOUND;
2012-06-23 11:42:40 +02:00
}
QByteArray cbuf = uz.fileData("META-INF/container.xml");
2012-05-26 14:26:10 +02:00
QDomDocument container;
int line, column;
QString err;
if (!container.setContent(cbuf, false, &err, &line, &column)) {
2012-05-26 14:26:10 +02:00
QString col, ln;
col.setNum(column);
ln.setNum(line);
QString error = err + "\n at line " + ln + " column " + col;
2012-06-23 11:42:40 +02:00
qDebug("loadCompressedMsc: read container error: %s\n", qPrintable(error));
2012-09-29 16:46:45 +02:00
return FILE_BAD_FORMAT;
2012-05-26 14:26:10 +02:00
}
// extract first rootfile
2012-06-23 11:42:40 +02:00
QDomNodeList nl = container.elementsByTagName("rootfile");
if (nl.isEmpty()) {
2012-06-11 12:15:21 +02:00
qDebug("can't find rootfile in: %s", qPrintable(name));
2012-09-29 16:46:45 +02:00
return FILE_NO_ROOTFILE;
2012-05-26 14:26:10 +02:00
}
2012-06-23 11:42:40 +02:00
QDomElement e = nl.at(0).toElement();
QString rootfile = e.attribute("full-path");
nl = container.elementsByTagName("file");
for (int i = 0; i < nl.size(); ++i) {
QString image = nl.at(i).toElement().text();
QByteArray dbuf = uz.fileData(image);
imageStore.add(image, dbuf);
}
2012-05-26 14:26:10 +02:00
QByteArray dbuf = uz.fileData(rootfile);
2012-06-11 12:15:21 +02:00
if (dbuf.isEmpty()) {
2012-06-23 11:42:40 +02:00
// qDebug("root file <%s> is empty", qPrintable(rootfile));
2012-06-11 12:15:21 +02:00
QList<QZipReader::FileInfo> fil = uz.fileInfoList();
foreach(const QZipReader::FileInfo& fi, fil) {
if (fi.filePath.endsWith(".mscx")) {
2012-06-11 12:15:21 +02:00
dbuf = uz.fileData(fi.filePath);
break;
}
}
}
2012-05-26 14:26:10 +02:00
QDomDocument doc;
if (!doc.setContent(dbuf, false, &err, &line, &column)) {
2012-05-26 14:26:10 +02:00
QString col, ln;
col.setNum(column);
ln.setNum(line);
QString error = err + "\n at line " + ln + " column " + col;
qDebug("error: %s", qPrintable(error));
2012-09-29 16:46:45 +02:00
return FILE_BAD_FORMAT;
2012-05-26 14:26:10 +02:00
}
docName = info.completeBaseName();
2012-09-29 16:46:45 +02:00
FileError retval = read1(doc.documentElement(), ignoreVersionError);
2012-05-26 14:26:10 +02:00
#ifdef OMR
//
// load OMR page images
//
if (_omr) {
int n = _omr->numPages();
for (int i = 0; i < n; ++i) {
QString path = QString("OmrPages/page%1.png").arg(i+1);
QByteArray dbuf = uz.fileData(path);
OmrPage* page = _omr->page(i);
QImage image;
if (image.loadFromData(dbuf, "PNG")) {
page->setImage(image);
2012-05-26 14:26:10 +02:00
}
else
qDebug("load image failed");
2012-05-26 14:26:10 +02:00
}
}
#endif
//
// read audio
//
if (_audio) {
QByteArray dbuf = uz.fileData("audio.ogg");
_audio->setData(dbuf);
2012-05-26 14:26:10 +02:00
}
return retval;
}
//---------------------------------------------------------
// loadMsc
// return true on success
//---------------------------------------------------------
2012-09-29 16:46:45 +02:00
Score::FileError Score::loadMsc(QString name, bool ignoreVersionError)
2012-05-26 14:26:10 +02:00
{
info.setFile(name);
2012-09-29 16:46:45 +02:00
if (name.endsWith(".mscz"))
return loadCompressedMsc(name, ignoreVersionError);
2012-05-26 14:26:10 +02:00
QFile f(name);
if (!f.open(QIODevice::ReadOnly)) {
MScore::lastError = f.errorString();
2012-09-29 16:46:45 +02:00
return FILE_OPEN_ERROR;
2012-05-26 14:26:10 +02:00
}
QDomDocument doc;
int line, column;
QString err;
if (!doc.setContent(&f, false, &err, &line, &column)) {
QString s = QT_TRANSLATE_NOOP("file", "error reading file %1 at line %2 column %3: %4\n");
MScore::lastError = s.arg(f.fileName()).arg(line).arg(column).arg(err);
2012-09-29 16:46:45 +02:00
return FILE_BAD_FORMAT;
2012-05-26 14:26:10 +02:00
}
f.close();
docName = f.fileName();
2012-09-29 16:46:45 +02:00
return read1(doc.documentElement(), ignoreVersionError);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// parseVersion
//---------------------------------------------------------
void Score::parseVersion(const QString& val)
{
QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
int v1, v2, v3, rv1, rv2, rv3;
if (re.indexIn(VERSION) != -1) {
QStringList sl = re.capturedTexts();
if (sl.size() == 4) {
v1 = sl[1].toInt();
v2 = sl[2].toInt();
v3 = sl[3].toInt();
if (re.indexIn(val) != -1) {
sl = re.capturedTexts();
if (sl.size() == 4) {
rv1 = sl[1].toInt();
rv2 = sl[2].toInt();
rv3 = sl[3].toInt();
int currentVersion = v1 * 10000 + v2 * 100 + v3;
int readVersion = rv1 * 10000 + rv2 * 100 + rv3;
if (readVersion > currentVersion) {
qDebug("read future version\n");
}
}
}
else {
QRegExp re1("(\\d+)\\.(\\d+)");
if (re1.indexIn(val) != -1) {
sl = re.capturedTexts();
if (sl.size() == 3) {
rv1 = sl[1].toInt();
rv2 = sl[2].toInt();
int currentVersion = v1 * 10000 + v2 * 100 + v3;
int readVersion = rv1 * 10000 + rv2 * 100;
if (readVersion > currentVersion) {
qDebug("read future version\n");
}
}
}
else
qDebug("1cannot parse <%s>\n", qPrintable(val));
}
}
}
else
qDebug("2cannot parse <%s>\n", VERSION);
}
//---------------------------------------------------------
// read1
// return true on success
//---------------------------------------------------------
2012-09-29 16:46:45 +02:00
Score::FileError Score::read1(const QDomElement& de, bool ignoreVersionError)
2012-05-26 14:26:10 +02:00
{
_elinks.clear();
for (QDomElement e = de; !e.isNull(); e = e.nextSiblingElement()) {
if (e.tagName() == "museScore") {
const QString& version = e.attribute("version");
QStringList sl = version.split('.');
_mscVersion = sl[0].toInt() * 100 + sl[1].toInt();
2012-09-29 16:46:45 +02:00
if (!ignoreVersionError) {
QString message;
if (_mscVersion > MSCVERSION)
return FILE_TOO_NEW;
if (_mscVersion < 114)
return FILE_TOO_OLD;
2012-05-26 14:26:10 +02:00
}
2012-07-04 13:04:54 +02:00
if (_mscVersion <= 114)
return read114(e);
2012-05-26 14:26:10 +02:00
for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) {
const QString& tag = ee.tagName();
const QString& val = ee.text();
if (tag == "programVersion") {
_mscoreVersion = val;
parseVersion(val);
}
else if (tag == "programRevision")
2012-05-26 14:26:10 +02:00
_mscoreRevision = val.toInt();
else if (tag == "Score")
2012-05-26 14:26:10 +02:00
read(ee);
else if (tag == "Revision") {
Revision* revision = new Revision;
revision->read(e);
_revisions->add(revision);
}
else
domError(ee);
}
}
else
domError(e);
}
int id = 1;
foreach(LinkedElements* le, _elinks)
le->setLid(this, id++);
_elinks.clear();
2012-10-02 17:07:28 +02:00
// _mscVersion is needed used during layout
// _mscVersion = MSCVERSION; // for later drag & drop usage
2012-09-29 16:46:45 +02:00
return FILE_NO_ERROR;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// read
// return false on error
//---------------------------------------------------------
bool Score::read(const QDomElement& de)
{
spanner.clear();
if (parentScore())
setMscVersion(parentScore()->mscVersion());
for (QDomElement ee = de.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) {
curTrack = -1;
const QString& tag(ee.tagName());
const QString& val(ee.text());
int i = val.toInt();
if (tag == "Staff")
readStaff(ee);
else if (tag == "KeySig") {
KeySig* ks = new KeySig(this);
ks->read(ee);
customKeysigs.append(ks);
}
else if (tag == "StaffType") {
int idx = ee.attribute("idx").toInt();
StaffType* ost = _staffTypes.value(idx);
StaffType* st;
if (ost)
st = ost;
else {
QString group = ee.attribute("group", "pitched");
if (group == "percussion")
st = new StaffTypePercussion();
else if (group == "tablature")
st = new StaffTypeTablature();
else
st = new StaffTypePitched();
}
st->read(ee);
if (idx < _staffTypes.size())
_staffTypes[idx] = st;
else
_staffTypes.append(st);
}
else if (tag == "siglist")
_sigmap->read(ee, _fileDivision);
else if (tag == "programVersion") {
_mscoreVersion = val;
parseVersion(val);
}
else if (tag == "programRevision")
_mscoreRevision = val.toInt();
else if (tag == "Omr") {
#ifdef OMR
_omr = new Omr(this);
_omr->read(ee);
#endif
}
else if (tag == "Audio") {
_audio = new Audio;
_audio->read(ee);
}
else if (tag == "showOmr")
_showOmr = i;
else if (tag == "playMode")
_playMode = PlayMode(i);
else if (tag == "LayerTag") {
int id = ee.attribute("id").toInt();
const QString& tag = ee.attribute("tag");
if (id >= 0 && id < 32) {
_layerTags[id] = tag;
_layerTagComments[id] = val;
}
}
else if (tag == "Layer") {
Layer layer;
layer.name = ee.attribute("name");
layer.tags = ee.attribute("mask").toUInt();
_layer.append(layer);
}
else if (tag == "currentLayer")
_currentLayer = val.toInt();
else if (tag == "SyntiSettings") {
_syntiState.clear();
_syntiState.read(ee);
}
else if (tag == "Spatium")
_style.setSpatium (val.toDouble() * MScore::DPMM); // obsolete, moved to Style
else if (tag == "page-offset") // obsolete, moved to Score
setPageNumberOffset(i);
else if (tag == "Division")
_fileDivision = i;
else if (tag == "showInvisible")
_showInvisible = i;
else if (tag == "showUnprintable")
_showUnprintable = i;
else if (tag == "showFrames")
_showFrames = i;
else if (tag == "showMargins")
_showPageborders = i;
else if (tag == "Style") {
qreal sp = _style.spatium();
_style.load(ee);
// if (_layoutMode == LayoutFloat || _layoutMode == LayoutSystem) {
if (_layoutMode == LayoutFloat) {
// style should not change spatium in
// float mode
_style.setSpatium(sp);
}
}
else if (tag == "copyright" || tag == "rights") {
Text* text = new Text(this);
text->read(ee);
setMetaTag("copyright", text->getText());
delete text;
}
else if (tag == "movement-number")
setMetaTag("movementNumber", val);
else if (tag == "movement-title")
setMetaTag("movementTitle", val);
else if (tag == "work-number")
setMetaTag("workNumber", val);
else if (tag == "work-title")
setMetaTag("workTitle", val);
else if (tag == "source")
setMetaTag("source", val);
else if (tag == "metaTag") {
QString name = ee.attribute("name");
setMetaTag(name, val);
}
else if (tag == "Part") {
Part* part = new Part(this);
part->read(ee);
_parts.push_back(part);
}
else if (tag == "Slur") {
Slur* slur = new Slur(this);
slur->read(ee);
spanner.append(slur);
}
else if (tag == "Excerpt") {
Excerpt* e = new Excerpt(this);
e->read(ee);
_excerpts.append(e);
}
else if (tag == "Beam") {
Beam* beam = new Beam(this);
beam->read(ee);
beam->setParent(0);
// _beams.append(beam);
}
else if (tag == "Score") { // recursion
Score* s = new Score(style());
s->setParentScore(this);
s->read(ee);
addExcerpt(s);
}
else if (tag == "PageList") {
for (QDomElement e = ee.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
if (e.tagName() == "Page") {
Page* page = new Page(this);
_pages.append(page);
page->read(e);
}
else
domError(e);
}
}
else if (tag == "name")
setName(val);
else
domError(ee);
}
// check slurs
foreach(Spanner* s, spanner) {
2012-09-13 18:01:34 +02:00
if (s->type() != Element::SLUR)
2012-05-26 14:26:10 +02:00
continue;
Slur* slur = static_cast<Slur*>(s);
if (!slur->startElement() || !slur->endElement()) {
qDebug("incomplete Slur\n");
if (slur->startElement()) {
qDebug(" front %d\n", static_cast<ChordRest*>(slur->startElement())->tick());
static_cast<ChordRest*>(slur->startElement())->removeSlurFor(slur);
}
if (slur->endElement()) {
qDebug(" back %d\n", static_cast<ChordRest*>(slur->endElement())->tick());
static_cast<ChordRest*>(slur->endElement())->removeSlurBack(slur);
}
}
else {
ChordRest* cr1 = (ChordRest*)(slur->startElement());
ChordRest* cr2 = (ChordRest*)(slur->endElement());
if (cr1->tick() > cr2->tick()) {
qDebug("Slur invalid start-end tick %d-%d\n", cr1->tick(), cr2->tick());
slur->setStartElement(cr2);
slur->setEndElement(cr1);
}
int n1 = 0;
int n2 = 0;
foreach(Spanner* s, cr1->spannerFor()) {
if (s == slur)
++n1;
}
foreach(Spanner* s, cr2->spannerBack()) {
if (s == slur)
++n2;
}
if (n1 != 1 || n2 != 1) {
qDebug("Slur references bad: %d %d\n", n1, n2);
}
}
}
spanner.clear();
connectTies();
searchSelectedElements();
_fileDivision = MScore::division;
//
// sanity check for barLineSpan
//
foreach(Staff* staff, _staves) {
int barLineSpan = staff->barLineSpan();
int idx = staffIdx(staff);
int n = nstaves();
if (idx + barLineSpan > n) {
qDebug("bad span: idx %d span %d staves %d\n", idx, barLineSpan, n);
staff->setBarLineSpan(n - idx);
}
}
if (_omr == 0)
_showOmr = false;
//
// check for soundfont,
// add default soundfont if none found
// (for compatibility with old scores)
//
bool hasSoundfont = false;
foreach(const SyntiParameter& sp, _syntiState) {
if (sp.name() == "soundfont") {
QFileInfo fi(sp.sval());
if(fi.exists())
hasSoundfont = true;
}
}
if (!hasSoundfont)
_syntiState.append(SyntiParameter("soundfont", MScore::soundFont));
fixTicks();
renumberMeasures();
rebuildMidiMapping();
updateChannel();
updateNotes(); // only for parts needed?
return true;
}
//---------------------------------------------------------
// print
//---------------------------------------------------------
void Score::print(QPainter* painter, int pageNo)
{
_printing = true;
Page* page = pages().at(pageNo);
QRectF fr = page->abbox();
QList<const Element*> ell = page->items(fr);
qStableSort(ell.begin(), ell.end(), elementLessThan);
foreach(const Element* e, ell) {
e->itemDiscovered = 0;
if (!e->visible())
continue;
painter->save();
painter->translate(e->pagePos());
e->draw(painter);
painter->restore();
}
_printing = false;
}
//---------------------------------------------------------
// readCompressedToBuffer
//---------------------------------------------------------
QByteArray Score::readCompressedToBuffer()
{
QZipReader uz(filePath());
2012-05-26 14:26:10 +02:00
QByteArray cbuf = uz.fileData("META-INF/container.xml");
2012-05-26 14:26:10 +02:00
QDomDocument container;
int line, column;
QString err;
if (!container.setContent(cbuf, false, &err, &line, &column)) {
2012-05-26 14:26:10 +02:00
QString col, ln;
col.setNum(column);
ln.setNum(line);
QString error = err + "\n at line " + ln + " column " + col;
qDebug("error: %s\n", qPrintable(error));
return QByteArray();
}
// extract first rootfile
QString rootfile = "";
QList<QString> images;
for (QDomElement e = container.documentElement(); !e.isNull(); e = e.nextSiblingElement()) {
if (e.tagName() != "container") {
domError(e);
continue;
}
for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) {
if (ee.tagName() != "rootfiles") {
domError(ee);
continue;
}
for (QDomElement eee = ee.firstChildElement(); !eee.isNull(); eee = eee.nextSiblingElement()) {
const QString& tag(eee.tagName());
const QString& val(eee.text());
if (tag == "rootfile") {
if (rootfile.isEmpty())
rootfile = eee.attribute(QString("full-path"));
}
else if (tag == "file")
images.append(val);
else
domError(eee);
}
}
}
//
// load images
//
foreach(const QString& s, images) {
QByteArray dbuf = uz.fileData(s);
imageStore.add(s, dbuf);
2012-05-26 14:26:10 +02:00
}
if (rootfile.isEmpty()) {
qDebug("can't find rootfile in: %s\n", qPrintable(filePath()));
return QByteArray();
}
return uz.fileData(rootfile);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// readToBuffer
//---------------------------------------------------------
QByteArray Score::readToBuffer()
{
QByteArray ba;
QString cs = info.suffix();
if (cs == "mscz") {
ba = readCompressedToBuffer();
}
if (cs.toLower() == "msc" || cs.toLower() == "mscx") {
QFile f(filePath());
if (f.open(QIODevice::ReadOnly)) {
ba = f.readAll();
f.close();
}
}
return ba;
}
//---------------------------------------------------------
// createRevision
//---------------------------------------------------------
void Score::createRevision()
{
#if 0
qDebug("createRevision\n");
QBuffer dbuf;
dbuf.open(QIODevice::ReadWrite);
saveFile(&dbuf, false, false);
dbuf.close();
QByteArray ba1 = readToBuffer();
QString os = QString::fromUtf8(ba1.data(), ba1.size());
QString ns = QString::fromUtf8(dbuf.buffer().data(), dbuf.buffer().size());
diff_match_patch dmp;
Revision* r = new Revision();
r->setDiff(dmp.patch_toText(dmp.patch_make(ns, os)));
r->setId("1");
_revisions->add(r);
// qDebug("patch:\n%s\n==========\n", qPrintable(patch));
//
#endif
}
//---------------------------------------------------------
// writeSegments
//---------------------------------------------------------
2012-08-09 12:12:43 +02:00
void Score::writeSegments(Xml& xml, const Measure* m, int strack, int etrack,
Segment* fs, Segment* ls,
2012-05-26 14:26:10 +02:00
bool writeSystemElements)
{
for (int track = strack; track < etrack; ++track) {
for (Segment* segment = fs; segment && segment != ls; segment = segment->next1()) {
if (track == 0)
segment->setWritten(false);
Element* e = segment->element(track);
//
// special case: - barline span > 1
// - part (excerpt) staff starts after
// barline element
bool needTick = segment->tick() != xml.curTick;
if ((segment->subtype() == Segment::SegEndBarLine)
2012-05-26 14:26:10 +02:00
&& (e == 0)
&& writeSystemElements
&& ((track % VOICES) == 0)) {
// search barline:
for (int idx = track - VOICES; idx >= 0; idx -= VOICES) {
if (segment->element(idx)) {
int oDiff = xml.trackDiff;
xml.trackDiff = idx; // staffIdx should be zero
segment->element(idx)->write(xml);
xml.trackDiff = oDiff;
break;
}
}
}
foreach (Element* e, segment->annotations()) {
if (e->track() != track || e->generated())
continue;
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
e->write(xml);
}
foreach (Spanner* e, segment->spannerFor()) {
if (e->track() == track && !e->generated()) {
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
e->setId(++xml.spannerId);
e->write(xml);
}
}
foreach(Spanner* e, segment->spannerBack()) {
if (e->track() == track && !e->generated()) {
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
xml.tagE(QString("endSpanner id=\"%1\"").arg(e->id()));
}
}
if (!e)
continue;
if (e->generated()) {
if ((xml.curTick - xml.tickDiff) == 0) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::CLEF) {
2012-05-26 14:26:10 +02:00
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
e->write(xml);
}
}
continue;
}
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
if (e->isChordRest()) {
ChordRest* cr = static_cast<ChordRest*>(e);
Beam* beam = cr->beam();
#ifndef NDEBUG
if (beam && beam->elements().front() == cr && (testMode() || !beam->generated())) {
beam->setId(xml.beamId++);
beam->write(xml);
}
#else
if (beam && !beam->generated() && beam->elements().front() == cr) {
beam->setId(xml.beamId++);
beam->write(xml);
}
#endif
cr->writeTuplet(xml);
foreach(Spanner* slur, cr->spannerFor()) {
bool found = false;
foreach(Spanner* slur1, spanner) {
if (slur1 == slur) {
found = true;
break;
}
}
if (!found) {
slur->setId(xml.spannerId++);
spanner.append(slur);
slur->write(xml);
}
}
foreach(Spanner* slur, cr->spannerBack()) {
bool found = false;
foreach(Spanner* slur1, spanner) {
if (slur1 == slur) {
found = true;
break;
}
}
if (!found) {
slur->setId(xml.spannerId++);
spanner.append(slur);
slur->write(xml);
}
}
}
if ((segment->subtype() == Segment::SegEndBarLine) && m && (m->multiMeasure() > 0)) {
2012-05-26 14:26:10 +02:00
xml.stag("BarLine");
xml.tag("subtype", m->endBarLineType());
xml.tag("visible", m->endBarLineVisible());
xml.etag();
}
else
e->write(xml);
segment->write(xml); // write only once
}
}
}
//---------------------------------------------------------
// searchTuplet
// search complete Dom for tuplet id
//---------------------------------------------------------
Tuplet* Score::searchTuplet(const QDomElement& de, int id)
{
QDomElement e = de;
QDomDocument doc = e.ownerDocument();
QString tag;
for (e = doc.documentElement(); !e.isNull(); e = e.nextSiblingElement()) {
tag = e.tagName();
if (tag == "museScore")
break;
}
if (tag != "museScore") {
qDebug("Score::searchTuplet(): no museScore found");
return 0;
}
for (e = e.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
tag = e.tagName();
if (tag == "Score" || tag == "Part")
break;
}
if (tag != "Score" && tag != "Part") {
qDebug("Score::searchTuplet(): no Score/Part found");
return 0;
}
if (tag == "Score")
e = e.firstChildElement();
else
e = e.nextSiblingElement();
for (; !e.isNull(); e = e.nextSiblingElement()) {
if (e.tagName() == "Staff") {
for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) {
if (ee.tagName() == "Measure") {
for (QDomElement eee = ee.firstChildElement(); !eee.isNull(); eee = eee.nextSiblingElement()) {
if (eee.tagName() == "Tuplet") {
Tuplet* tuplet = new Tuplet(this);
QList<Spanner*> spannerList;
QList<Tuplet*> tuplets;
tuplet->read(eee, &tuplets, &spannerList);
if (tuplet->id() == id)
return tuplet;
delete tuplet;
}
}
}
}
}
}
return 0;
}