MuseScore/libmscore/scorefile.cpp

1351 lines
48 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
2012-11-19 09:29:46 +01:00
// Copyright (C) 2011-2012 Werner Schweer and others
2012-05-26 14:26:10 +02:00
//
// 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"
2013-01-04 17:48:15 +01:00
#include "chord.h"
2012-05-26 14:26:10 +02:00
#include "tuplet.h"
#include "beam.h"
#include "revisions.h"
#include "page.h"
#include "part.h"
#include "staff.h"
2013-06-10 11:03:34 +02:00
#include "system.h"
2012-05-26 14:26:10 +02:00
#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 "barline.h"
#include "libmscore/qzipreader_p.h"
#include "libmscore/qzipwriter_p.h"
#ifdef Q_OS_WIN
2013-04-18 15:47:07 +02:00
#include <windows.h>
#include <stdio.h>
#endif
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
//---------------------------------------------------------
// writeMeasure
//---------------------------------------------------------
static void writeMeasure(Xml& xml, MeasureBase* m, int staffIdx, bool writeSystemElements)
{
if (m->type() == Element::MEASURE || staffIdx == 0)
m->write(xml, staffIdx, writeSystemElements);
if (m->type() == Element::MEASURE)
xml.curTick = m->tick() + m->ticks();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Score::write(Xml& xml, bool selectionOnly)
{
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 (!MScore::testMode)
_synthesizerState.write(xml);
2012-05-26 14:26:10 +02:00
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) {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if ((idx >= STAFF_TYPES) || !(*st)->isEqual(*StaffType::preset(idx)))
(*st)->write(xml, idx);
2012-05-26 14:26:10 +02:00
++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();
if (!MScore::testMode || (i.key() != "platform" && i.key() != "creationDate"))
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();
2013-08-01 15:14:24 +02:00
if (_selection.endSegment())
measureEnd = _selection.endSegment()->measure()->next();
else
measureEnd = 0;
2012-05-26 14:26:10 +02:00
}
else {
staffStart = 0;
staffEnd = nstaves();
measureStart = first();
measureEnd = 0;
}
xml.trackDiff = -staffStart * VOICES;
if (measureStart) {
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;
bool writeSystemElements = staffIdx == staffStart;
for (MeasureBase* m = measureStart; m != measureEnd; m = m->next())
writeMeasure(xml, m, staffIdx, writeSystemElements);
xml.etag();
2012-05-26 14:26:10 +02:00
}
}
xml.curTrack = -1;
if (!selectionOnly) {
2012-12-18 09:09:11 +01:00
foreach(Excerpt* excerpt, _excerpts) {
if (excerpt->score() != this)
excerpt->score()->write(xml, false); // recursion
}
2012-05-26 14:26:10 +02:00
}
if (parentScore())
xml.tag("name", name());
xml.etag();
}
//---------------------------------------------------------
// readStaff
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Score::readStaff(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
MeasureBase* mb = first();
2013-01-11 18:10:18 +01:00
int staff = e.intAttribute("id", 1) - 1;
2013-01-21 20:21:41 +01:00
e.setTick(0);
e.setTrack(staff * VOICES);
2012-05-26 14:26:10 +02:00
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "Measure") {
Measure* measure = 0;
if (staff == 0) {
measure = new Measure(this);
2013-01-21 20:21:41 +01:00
measure->setTick(e.tick());
2012-05-26 14:26:10 +02:00
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);
2013-01-21 20:21:41 +01:00
measure->setTick(e.tick());
2012-05-26 14:26:10 +02:00
add(measure);
}
}
measure->read(e, staff);
2013-01-21 20:21:41 +01:00
e.setTick(measure->tick() + measure->ticks());
2012-05-26 14:26:10 +02:00
}
else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
2013-01-18 10:55:52 +01:00
MeasureBase* mb = static_cast<MeasureBase*>(Element::name2Element(tag, this));
2012-05-26 14:26:10 +02:00
mb->read(e);
2013-01-21 20:21:41 +01:00
mb->setTick(e.tick());
2012-05-26 14:26:10 +02:00
add(mb);
}
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// 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 (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 {
2013-05-13 10:27:06 +02:00
if (suffix == "mscx")
2012-05-26 14:26:10 +02:00
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 {
2013-05-13 10:27:06 +02:00
if (suffix == "mscx")
2012-05-26 14:26:10 +02:00
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"));
}
}
#ifdef Q_OS_WIN
2013-04-18 15:47:07 +02:00
QFileInfo fileBackup(dir, backupName);
QString backupNativePath = QDir::toNativeSeparators(fileBackup.absoluteFilePath());
SetFileAttributes((LPCTSTR)backupNativePath.toLocal8Bit(), FILE_ATTRIBUTE_HIDDEN);
#endif
2012-05-26 14:26:10 +02:00
//
// 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)
{
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)
{
2013-07-26 15:39:46 +02:00
MQZipWriter 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);
2013-05-12 12:43:22 +02:00
const QImage& image = page->image();
2012-05-26 14:26:10 +02:00
if (!image.save(&cbuf, "PNG"))
2013-05-12 12:43:22 +02:00
throw(QString("save file: cannot save image (%1x%2)").arg(image.width()).arg(image.height()));
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)) {
2013-03-18 15:14:05 +01:00
undo(new ChangeStyle(this, st));
2012-05-26 14:26:10 +02:00
return true;
}
else {
MScore::lastError = tr("The style file is not compatible with this version of MuseScore.");
return false;
}
2012-05-26 14:26:10 +02:00
}
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;
extern bool enableTestMode;
2012-05-26 14:26:10 +02:00
void Score::saveFile(QIODevice* f, bool msczFormat, bool onlySelection)
{
if(!MScore::testMode)
MScore::testMode = enableTestMode;
2012-05-26 14:26:10 +02:00
Xml xml(f);
xml.writeOmr = msczFormat;
xml.header();
xml.stag("museScore version=\"" MSC_VERSION "\"");
if (!MScore::testMode) {
2012-05-26 14:26:10 +02:00
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
{
2013-07-26 15:39:46 +02:00
MQZipReader 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
2013-01-11 18:10:18 +01:00
QString rootfile;
XmlReader e(cbuf);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
2013-01-11 18:10:18 +01:00
if (tag == "rootfile") {
rootfile = e.attribute("full-path");
e.skipCurrentElement();
}
else if (tag == "file") {
QString image(e.readElementText());
QByteArray dbuf = uz.fileData(image);
imageStore.add(image, dbuf);
}
2012-06-23 11:42:40 +02:00
}
2013-01-11 18:10:18 +01:00
if (rootfile.isEmpty())
return FILE_NO_ROOTFILE;
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));
2013-07-26 15:39:46 +02:00
QList<MQZipReader::FileInfo> fil = uz.fileInfoList();
foreach(const MQZipReader::FileInfo& fi, fil) {
if (fi.filePath.endsWith(".mscx")) {
2012-06-11 12:15:21 +02:00
dbuf = uz.fileData(fi.filePath);
break;
}
}
}
2013-01-11 18:10:18 +01:00
e.clear();
e.addData(dbuf);
e.setDocName(info.completeBaseName());
2012-05-26 14:26:10 +02:00
2013-01-11 18:10:18 +01:00
FileError retval = read1(e, ignoreVersionError);
2013-05-27 15:59:43 +02:00
_noteHeadWidth = symbols[_symIdx][quartheadSym].width(spatium() / (MScore::DPI * SPATIUM20));
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
}
2013-01-11 18:10:18 +01:00
XmlReader xml(&f);
2013-05-27 15:59:43 +02:00
FileError retval = read1(xml, ignoreVersionError);
_noteHeadWidth = symbols[_symIdx][quartheadSym].width(spatium() / (MScore::DPI * SPATIUM20));
return retval;
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
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
Score::FileError Score::read1(XmlReader& e, bool ignoreVersionError)
2012-05-26 14:26:10 +02:00
{
_elinks.clear();
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (e.name() == "museScore") {
2012-05-26 14:26:10 +02:00
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);
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "programVersion") {
2013-01-11 18:10:18 +01:00
_mscoreVersion = e.readElementText();
parseVersion(_mscoreVersion);
2012-05-26 14:26:10 +02:00
}
else if (tag == "programRevision")
2013-01-11 18:10:18 +01:00
_mscoreRevision = e.readInt();
2013-06-26 10:32:45 +02:00
else if (tag == "Score") {
if (!read(e))
return FILE_BAD_FORMAT;
}
2012-05-26 14:26:10 +02:00
else if (tag == "Revision") {
Revision* revision = new Revision;
revision->read(e);
_revisions->add(revision);
}
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
int id = 1;
2013-05-16 12:48:23 +02:00
foreach (LinkedElements* le, _elinks)
2012-05-26 14:26:10 +02:00
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
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
bool Score::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
if (parentScore())
setMscVersion(parentScore()->mscVersion());
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
2013-01-21 20:21:41 +01:00
e.setTrack(-1);
2013-01-11 18:10:18 +01:00
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "Staff")
2013-01-11 18:10:18 +01:00
readStaff(e);
2012-05-26 14:26:10 +02:00
else if (tag == "KeySig") {
KeySig* ks = new KeySig(this);
2013-01-11 18:10:18 +01:00
ks->read(e);
2012-05-26 14:26:10 +02:00
customKeysigs.append(ks);
}
else if (tag == "StaffType") {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
int idx = e.intAttribute("idx");
QString groupName = e.attribute("group", "pitched");
int group;
// staff type numbering did change!
// attempt to keep some compatibility with existing 2.0 scores
if (groupName == "percussion")
group = PERCUSSION_STAFF_GROUP;
else if (groupName == "tablature")
group = TAB_STAFF_GROUP;
else group = STANDARD_STAFF_GROUP;
StaffType* ost = staffType(idx);
2012-05-26 14:26:10 +02:00
StaffType* st;
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
if (ost && ost->group() == group)
st = ost->clone();
2012-05-26 14:26:10 +02:00
else {
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
idx = -1;
switch (group)
{
case PERCUSSION_STAFF_GROUP:
2012-05-26 14:26:10 +02:00
st = new StaffTypePercussion();
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
break;
case TAB_STAFF_GROUP:
2012-05-26 14:26:10 +02:00
st = new StaffTypeTablature();
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
break;
default:
2012-05-26 14:26:10 +02:00
st = new StaffTypePitched();
}
Re-factor presets and staff types. 1) Built-in staff types have been removed. 2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications. 3) Each new score is given by default one staff type for each preset with the same name. 4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet). 5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type. 6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5) 7) The Staff Type Editor lists all the staff types This should ensure consistency among the several lists of staff types and avoid duplication of similar items Terminology: 7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want. 8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard" 9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are: libmscore/score.cpp libmscore/stafftype.cpp/.h mscore/editstafftype.cpp (code for naming a new staff type) mscore/instrdialog.cpp (building type list) Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
}
2013-01-11 18:10:18 +01:00
st->read(e);
st->setBuiltin(false);
addStaffType(idx, st);
2012-05-26 14:26:10 +02:00
}
else if (tag == "siglist")
2013-01-11 18:10:18 +01:00
_sigmap->read(e, _fileDivision);
2012-05-26 14:26:10 +02:00
else if (tag == "programVersion") {
2013-01-11 18:10:18 +01:00
_mscoreVersion = e.readElementText();
parseVersion(_mscoreVersion);
2012-05-26 14:26:10 +02:00
}
else if (tag == "programRevision")
2013-01-11 18:10:18 +01:00
_mscoreRevision = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "Omr") {
#ifdef OMR
_omr = new Omr(this);
2013-01-11 18:10:18 +01:00
_omr->read(e);
#else
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
#endif
}
else if (tag == "Audio") {
_audio = new Audio;
2013-01-11 18:10:18 +01:00
_audio->read(e);
2012-05-26 14:26:10 +02:00
}
else if (tag == "showOmr")
2013-01-11 18:10:18 +01:00
_showOmr = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "playMode")
2013-01-11 18:10:18 +01:00
_playMode = PlayMode(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "LayerTag") {
2013-01-11 18:10:18 +01:00
int id = e.intAttribute("id");
const QString& tag = e.attribute("tag");
QString val(e.readElementText());
2012-05-26 14:26:10 +02:00
if (id >= 0 && id < 32) {
_layerTags[id] = tag;
_layerTagComments[id] = val;
}
}
else if (tag == "Layer") {
Layer layer;
2013-01-11 18:10:18 +01:00
layer.name = e.attribute("name");
layer.tags = e.attribute("mask").toUInt();
2012-05-26 14:26:10 +02:00
_layer.append(layer);
}
else if (tag == "currentLayer")
2013-01-11 18:10:18 +01:00
_currentLayer = e.readInt();
else if (tag == "SyntiSettings") // obsolete
_synthesizerState.read(e);
else if (tag == "Synthesizer")
_synthesizerState.read(e);
2012-05-26 14:26:10 +02:00
else if (tag == "Spatium")
2013-01-11 18:10:18 +01:00
_style.setSpatium (e.readDouble() * MScore::DPMM); // obsolete, moved to Style
2012-05-26 14:26:10 +02:00
else if (tag == "page-offset") // obsolete, moved to Score
2013-01-11 18:10:18 +01:00
setPageNumberOffset(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "Division")
2013-01-11 18:10:18 +01:00
_fileDivision = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "showInvisible")
2013-01-11 18:10:18 +01:00
_showInvisible = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "showUnprintable")
2013-01-11 18:10:18 +01:00
_showUnprintable = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "showFrames")
2013-01-11 18:10:18 +01:00
_showFrames = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "showMargins")
2013-01-11 18:10:18 +01:00
_showPageborders = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "Style") {
qreal sp = _style.spatium();
2013-01-11 18:10:18 +01:00
_style.load(e);
2012-05-26 14:26:10 +02:00
// 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);
2013-01-11 18:10:18 +01:00
text->read(e);
2013-02-26 15:50:36 +01:00
setMetaTag("copyright", text->text());
2012-05-26 14:26:10 +02:00
delete text;
}
else if (tag == "movement-number")
2013-01-11 18:10:18 +01:00
setMetaTag("movementNumber", e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "movement-title")
2013-01-11 18:10:18 +01:00
setMetaTag("movementTitle", e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "work-number")
2013-01-11 18:10:18 +01:00
setMetaTag("workNumber", e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "work-title")
2013-01-11 18:10:18 +01:00
setMetaTag("workTitle", e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "source")
2013-01-11 18:10:18 +01:00
setMetaTag("source", e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "metaTag") {
2013-01-11 18:10:18 +01:00
QString name = e.attribute("name");
setMetaTag(name, e.readElementText());
2012-05-26 14:26:10 +02:00
}
else if (tag == "Part") {
Part* part = new Part(this);
2013-01-11 18:10:18 +01:00
part->read(e);
2012-05-26 14:26:10 +02:00
_parts.push_back(part);
}
2013-06-10 11:03:34 +02:00
else if ((tag == "HairPin")
|| (tag == "Ottava")
|| (tag == "TextLine")
|| (tag == "Volta")
|| (tag == "Trill")
|| (tag == "Slur")
|| (tag == "Pedal")) {
Spanner* s = static_cast<Spanner*>(Element::name2Element(tag, this));
s->read(e);
2013-06-20 13:57:15 +02:00
addSpanner(s);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Excerpt") {
2013-01-11 18:10:18 +01:00
Excerpt* ex = new Excerpt(this);
ex->read(e);
_excerpts.append(ex);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Beam") {
Beam* beam = new Beam(this);
2013-01-11 18:10:18 +01:00
beam->read(e);
2012-05-26 14:26:10 +02:00
beam->setParent(0);
// _beams.append(beam);
}
else if (tag == "Score") { // recursion
Score* s = new Score(style());
s->setParentScore(this);
2013-01-11 18:10:18 +01:00
s->read(e);
2012-05-26 14:26:10 +02:00
addExcerpt(s);
}
else if (tag == "PageList") {
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (e.name() == "Page") {
2012-05-26 14:26:10 +02:00
Page* page = new Page(this);
_pages.append(page);
page->read(e);
}
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
else if (tag == "name")
2013-01-11 18:10:18 +01:00
setName(e.readElementText());
else if (tag == "page-layout") { // obsolete
if (_layoutMode != LayoutFloat && _layoutMode != LayoutSystem) {
PageFormat pf;
pf.copy(*pageFormat());
pf.read(e);
setPageFormat(pf);
}
else
e.skipCurrentElement();
}
else if (tag == "cursorTrack")
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
2013-06-26 10:32:45 +02:00
if (e.error() != QXmlStreamReader::NoError)
return false;
2012-05-26 14:26:10 +02:00
connectTies();
_fileDivision = MScore::division;
//
// sanity check for barLineSpan
//
foreach(Staff* st, _staves) {
int barLineSpan = st->barLineSpan();
int idx = staffIdx(st);
2012-05-26 14:26:10 +02:00
int n = nstaves();
if (idx + barLineSpan > n) {
qDebug("bad span: idx %d span %d staves %d\n", idx, barLineSpan, n);
st->setBarLineSpan(n - idx);
}
// check spanFrom
if(st->barLineFrom() < MIN_BARLINE_SPAN_FROMTO)
st->setBarLineFrom(MIN_BARLINE_SPAN_FROMTO);
if(st->barLineFrom() > st->lines()*2)
st->setBarLineFrom(st->lines()*2);
// check spanTo
Staff* stTo = st->barLineSpan() <= 1 ? st : staff(idx + st->barLineSpan() - 1);
// 1-line staves have special bar line spans
int maxBarLineTo = stTo->lines() == 1 ? BARLINE_SPAN_1LINESTAFF_TO : stTo->lines()*2;
int defaultBarLineTo = stTo->lines() == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (stTo->lines() - 1) * 2;
if (st->barLineTo() == UNKNOWN_BARLINE_TO)
st->setBarLineTo(defaultBarLineTo);
if (st->barLineTo() < MIN_BARLINE_SPAN_FROMTO)
st->setBarLineTo(MIN_BARLINE_SPAN_FROMTO);
if (st->barLineTo() > maxBarLineTo)
st->setBarLineTo(maxBarLineTo);
// on single staff span, check spanFrom and spanTo are distant enough
if (st->barLineSpan() == 1) {
if(st->barLineTo() - st->barLineFrom() < MIN_BARLINE_FROMTO_DIST) {
st->setBarLineFrom(0);
st->setBarLineTo(defaultBarLineTo);
}
2012-05-26 14:26:10 +02:00
}
}
if (_omr == 0)
_showOmr = false;
fixTicks();
rebuildMidiMapping();
updateChannel();
updateNotes(); // only for parts needed?
2012-11-19 09:29:46 +01:00
createPlayEvents();
2013-07-25 17:22:49 +02:00
setExcerptsChanged(false);
2012-05-26 14:26:10 +02:00
return true;
}
//---------------------------------------------------------
// print
//---------------------------------------------------------
void Score::print(QPainter* painter, int pageNo)
{
_printing = true;
Page* page = pages().at(pageNo);
QRectF fr = page->abbox();
QList<Element*> ell = page->items(fr);
2012-05-26 14:26:10 +02:00
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()
{
2013-07-26 15:39:46 +02:00
MQZipReader 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
2013-01-11 18:10:18 +01:00
XmlReader e(cbuf);
QString rootfile;
2012-05-26 14:26:10 +02:00
QList<QString> images;
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (e.name() != "container") {
e.unknown();
2012-05-26 14:26:10 +02:00
continue;
}
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (e.name() != "rootfiles") {
e.unknown();
2012-05-26 14:26:10 +02:00
continue;
}
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "rootfile") {
if (rootfile.isEmpty())
2013-01-11 18:10:18 +01:00
rootfile = e.attribute("full-path");
2012-05-26 14:26:10 +02:00
}
else if (tag == "file")
2013-01-11 18:10:18 +01:00
images.append(e.readElementText());
2012-05-26 14:26:10 +02:00
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
}
//
// 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
2013-01-07 15:39:28 +01:00
// ls - write upto this segment (excluding)
// can be zero
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-10-13 13:06:32 +02:00
void Score::writeSegments(Xml& xml, int strack, int etrack,
Segment* fs, Segment* ls, bool writeSystemElements, bool clip, bool needFirstTick)
2012-05-26 14:26:10 +02:00
{
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 = (needFirstTick && segment == fs) || (segment->tick() != xml.curTick);
if ((segment->segmentType() == 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);
}
2013-06-10 11:03:34 +02:00
if (segment->segmentType() & (Segment::SegChordRest)) {
2013-07-05 11:23:52 +02:00
for (auto i : _spanner.map()) { // TODO: dont search whole list
2013-06-20 13:57:15 +02:00
Spanner* s = i.second;
if (s->generated())
2013-06-20 18:48:28 +02:00
continue;
2013-10-13 13:06:32 +02:00
if (s->id() == -1)
s->setId(++xml.spannerId);
if (s->track() == track) {
int endTick = ls == 0 ? lastMeasure()->endTick() : ls->tick();
if (s->tick() == segment->tick() && (!clip || s->tick2() < endTick)) {
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
}
s->write(xml);
2013-06-10 11:03:34 +02:00
}
2012-05-26 14:26:10 +02:00
}
else if (s->tick2() == segment->tick()
&& (s->track2() == track)
&& (!clip || s->tick() >= fs->tick())) {
2013-06-20 18:48:28 +02:00
if (needTick) {
xml.tag("tick", segment->tick() - xml.tickDiff);
xml.curTick = segment->tick();
needTick = false;
2013-06-10 11:03:34 +02:00
}
2013-06-20 18:48:28 +02:00
xml.tagE(QString("endSpanner id=\"%1\"").arg(s->id()));
2012-05-26 14:26:10 +02:00
}
}
}
2013-06-10 11:03:34 +02:00
2012-05-26 14:26:10 +02:00
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);
2013-06-19 16:25:29 +02:00
cr->writeBeam(xml);
2012-05-26 14:26:10 +02:00
cr->writeTuplet(xml);
}
#if 0 // TODO MM
if ((segment->segmentType() == 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
#endif
e->write(xml);
2012-05-26 14:26:10 +02:00
segment->write(xml); // write only once
}
}
}
//---------------------------------------------------------
// searchTuplet
// search complete Dom for tuplet id
2013-01-17 14:55:04 +01:00
// last resort in case of error
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
Tuplet* Score::searchTuplet(XmlReader& /*e*/, int /*id*/)
2012-05-26 14:26:10 +02:00
{
2013-01-11 18:10:18 +01:00
#if 0 // TODOx
2012-05-26 14:26:10 +02:00
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);
2012-05-26 14:26:10 +02:00
if (tuplet->id() == id)
return tuplet;
delete tuplet;
}
}
}
}
}
}
2013-01-11 18:10:18 +01:00
#endif
2012-05-26 14:26:10 +02:00
return 0;
}
2013-05-13 18:49:17 +02:00
}