MuseScore/libmscore/excerpt.cpp

440 lines
19 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2009-2011 Werner Schweer
//
// 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 LICENCE.GPL
//=============================================================================
#include "excerpt.h"
#include "score.h"
#include "part.h"
#include "xml.h"
#include "staff.h"
#include "box.h"
#include "style.h"
#include "page.h"
#include "text.h"
#include "slur.h"
#include "sig.h"
#include "tempo.h"
#include "measure.h"
#include "rest.h"
#include "stafftype.h"
#include "tuplet.h"
#include "chord.h"
#include "note.h"
#include "lyrics.h"
#include "segment.h"
#include "tupletmap.h"
#include "tiemap.h"
#include "spannermap.h"
#include "layoutbreak.h"
namespace Ms {
//---------------------------------------------------------
// read
//---------------------------------------------------------
void Excerpt::read(XmlReader& e)
{
const QList<Part*>& pl = _score->parts();
QString name;
while (e.readNextStartElement()) {
const QStringRef& tag = e.name();
if (tag == "name")
name = e.readElementText();
else if (tag == "title")
_title = e.readElementText().trimmed();
else if (tag == "part") {
int partIdx = e.readInt();
if (partIdx < 0 || partIdx >= pl.size())
qDebug("Excerpt::read: bad part index");
else
_parts.append(pl.at(partIdx));
}
}
if (_title.isEmpty())
_title = name.trimmed();
}
//---------------------------------------------------------
// operator!=
//---------------------------------------------------------
bool Excerpt::operator!=(const Excerpt& e) const
{
if (e._score != _score)
return true;
if (e._title != _title)
return true;
if (e._parts != _parts)
return true;
return false;
}
//---------------------------------------------------------
// localSetScore
//---------------------------------------------------------
static void localSetScore(void* score, Element* element)
{
element->setScore((Score*)score);
}
//---------------------------------------------------------
// createExcerpt
//---------------------------------------------------------
Score* createExcerpt(const QList<Part*>& parts)
{
if (parts.isEmpty())
return 0;
QList<int> srcStaves;
Score* oscore = parts.front()->score();
Score* score = new Score(oscore);
// clone layer:
for (int i = 0; i < 32; ++i) {
score->layerTags()[i] = oscore->layerTags()[i];
score->layerTagComments()[i] = oscore->layerTagComments()[i];
}
score->setCurrentLayer(oscore->currentLayer());
score->layer().clear();
foreach(const Layer& l, oscore->layer())
score->layer().append(l);
score->setPageNumberOffset(oscore->pageNumberOffset());
foreach (Part* part, parts) {
Part* p = new Part(score);
p->setInstrument(*part->instr());
int idx = 0;
foreach(Staff* staff, *part->staves()) {
Staff* s = new Staff(score, p, idx);
s->setStaffType(staff->staffType());
s->setUpdateKeymap(true);
s->linkTo(staff);
p->staves()->append(s);
score->staves().append(s);
srcStaves.append(oscore->staffIdx(staff));
++idx;
}
score->appendPart(p);
}
cloneStaves(oscore, score, srcStaves);
//
// create excerpt title
//
MeasureBase* measure = score->first();
if (!measure || (measure->type() != Element::VBOX)) {
MeasureBase* nmeasure = new VBox(score);
nmeasure->setTick(0);
score->addMeasure(nmeasure, measure);
measure = nmeasure;
}
Text* txt = new Text(score);
txt->setTextStyleType(TEXT_STYLE_INSTRUMENT_EXCERPT);
txt->setText(parts.front()->longName().toPlainText());
measure->add(txt);
//
// layout score
//
score->setPlaylistDirty(true);
score->rebuildMidiMapping();
score->updateChannel();
score->setLayoutAll(true);
score->addLayoutFlags(LayoutFlags(LAYOUT_FIX_TICKS | LAYOUT_FIX_PITCH_VELO));
score->doLayout();
return score;
}
//---------------------------------------------------------
// cloneStaves
//---------------------------------------------------------
void cloneStaves(Score* oscore, Score* score, const QList<int>& map)
{
TieMap tieMap;
MeasureBaseList* nmbl = score->measures();
for (MeasureBase* mb = oscore->measures()->first(); mb; mb = mb->next()) {
MeasureBase* nmb = 0;
if (mb->type() == Element::HBOX)
nmb = new HBox(score);
else if (mb->type() == Element::VBOX)
nmb = new VBox(score);
else if (mb->type() == Element::MEASURE) {
Measure* m = static_cast<Measure*>(mb);
Measure* nm = new Measure(score);
nmb = nm;
nm->setTick(m->tick());
nm->setLen(m->len());
nm->setTimesig(m->timesig());
nm->setRepeatCount(m->repeatCount());
nm->setRepeatFlags(m->repeatFlags());
nm->setIrregular(m->irregular());
nm->setNo(m->no());
nm->setNoOffset(m->noOffset());
nm->setBreakMultiMeasureRest(m->breakMultiMeasureRest());
nm->setEndBarLineType(
m->endBarLineType(),
m->endBarLineGenerated(),
m->endBarLineVisible(),
m->endBarLineColor());
// Fraction ts = nm->len();
int tracks = oscore->nstaves() * VOICES;
for (int srcTrack = 0; srcTrack < tracks; ++srcTrack) {
TupletMap tupletMap; // tuplets cannot cross measure boundaries
int track = -1;
int st = 0;
foreach(int staff, map) {
if (staff == srcTrack/VOICES) {
track = (st * VOICES) + srcTrack % VOICES;
break;
}
++st;
}
if (((srcTrack % VOICES) == 0) && track != -1) {
}
for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) {
Segment* ns = nm->getSegment(oseg->segmentType(), oseg->tick());
foreach (Element* e, oseg->annotations()) {
if (e->generated())
continue;
if ((e->track() == srcTrack && track != -1)
|| (e->systemFlag() && srcTrack == 0)
) {
Element* ne = e->clone();
ne->setUserOff(QPointF()); // reset user offset as most likely
// it will not fit
ne->setReadPos(QPointF());
ne->setTrack(track == -1 ? 0 : track);
ns->add(ne);
}
}
if (track == -1)
continue;
Element* oe = oseg->element(srcTrack);
if (oe == 0)
continue;
Element* ne;
if (oe->generated() || oe->type() == Element::CLEF)
ne = oe->clone();
else
ne = oe->linkedClone();
ne->setTrack(track);
ne->scanElements(score, localSetScore); //necessary?
ne->setScore(score);
if (oe->isChordRest()) {
ChordRest* ocr = static_cast<ChordRest*>(oe);
ChordRest* ncr = static_cast<ChordRest*>(ne);
Tuplet* ot = ocr->tuplet();
if (ot) {
Tuplet* nt = tupletMap.findNew(ot);
if (nt == 0) {
nt = new Tuplet(*ot);
nt->clear();
nt->setTrack(track);
nt->setScore(score);
tupletMap.add(ot, nt);
}
nt->add(ncr);
ncr->setTuplet(nt);
}
if (oe->type() == Element::CHORD) {
Chord* och = static_cast<Chord*>(ocr);
Chord* nch = static_cast<Chord*>(ncr);
int n = och->notes().size();
for (int i = 0; i < n; ++i) {
Note* on = och->notes().at(i);
Note* nn = nch->notes().at(i);
if (on->tieFor()) {
Tie* tie = new Tie(score);
nn->setTieFor(tie);
tie->setStartNote(nn);
tie->setTrack(nn->track());
tieMap.add(on->tieFor(), tie);
}
if (on->tieBack()) {
Tie* tie = tieMap.findNew(on->tieBack());
if (tie) {
nn->setTieBack(tie);
tie->setEndNote(nn);
}
else {
qDebug("cloneStave: cannot find tie");
}
}
}
}
}
ns->add(ne);
}
}
}
foreach(Element* e, *mb->el()) {
if (e->type() == Element::LAYOUT_BREAK) {
LayoutBreakType st = static_cast<LayoutBreak*>(e)->layoutBreakType();
if (st == LAYOUT_BREAK_PAGE || st == LAYOUT_BREAK_LINE)
continue;
}
Element* ne = e->clone();
ne->setScore(score);
nmb->add(ne);
}
nmbl->add(nmb);
}
int n = map.size();
for (int dstStaffIdx = 0; dstStaffIdx < n; ++dstStaffIdx) {
Staff* srcStaff = oscore->staff(map[dstStaffIdx]);
Staff* dstStaff = score->staff(dstStaffIdx);
if (srcStaff->primaryStaff()) {
int span = srcStaff->barLineSpan();
int sIdx = srcStaff->idx();
int eIdx = sIdx + span;
for (int staffIdx = sIdx; staffIdx < eIdx; ++staffIdx) {
if (!map.contains(staffIdx))
--span;
}
dstStaff->setBarLineSpan(span);
int idx = 0;
foreach(BracketItem bi, srcStaff->brackets()) {
dstStaff->setBracket(idx, bi._bracket);
dstStaff->setBracketSpan(idx, bi._bracketSpan);
}
}
}
for (auto i : oscore->spanner()) {
Spanner* s = i.second;
int staffIdx = s->staffIdx();
int dstTrack = -1;
int st = 0;
//always export voltas to first staff in part
if(s->type() == Element::VOLTA)
dstTrack = s->voice();
else { //export other spanner if staffidx matches
for (int index : map) {
if (index == staffIdx) {
dstTrack = st * VOICES + s->voice();
break;
}
++st;
}
}
if (dstTrack == -1)
continue;
Spanner* ns = static_cast<Spanner*>(s->linkedClone());
ns->setScore(score);
ns->setParent(0);
ns->setTrack(dstTrack);
score->addSpanner(ns);
}
}
//---------------------------------------------------------
// cloneStaff
// srcStaff and dstStaff are in the same score
//---------------------------------------------------------
void cloneStaff(Staff* srcStaff, Staff* dstStaff)
{
Score* score = srcStaff->score();
TieMap tieMap;
int srcStaffIdx = score->staffIdx(srcStaff);
int dstStaffIdx = score->staffIdx(dstStaff);
for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) {
int sTrack = srcStaffIdx * VOICES;
int eTrack = sTrack + VOICES;
for (int srcTrack = sTrack; srcTrack < eTrack; ++srcTrack) {
TupletMap tupletMap; // tuplets cannot cross measure boundaries
int dstTrack = dstStaffIdx * VOICES + (srcTrack - sTrack);
for (Segment* seg = m->first(); seg; seg = seg->next()) {
Element* oe = seg->element(srcTrack);
if (oe == 0)
continue;
Element* ne;
if (oe->generated() || oe->type() == Element::CLEF)
ne = oe->clone();
else
ne = oe->linkedClone();
ne->setTrack(dstTrack);
seg->add(ne);
if (oe->isChordRest()) {
ChordRest* ocr = static_cast<ChordRest*>(oe);
ChordRest* ncr = static_cast<ChordRest*>(ne);
Tuplet* ot = ocr->tuplet();
if (ot) {
Tuplet* nt = tupletMap.findNew(ot);
if (nt == 0) {
nt = new Tuplet(*ot);
nt->clear();
nt->setTrack(dstTrack);
nt->setParent(m);
tupletMap.add(ot, nt);
}
ncr->setTuplet(nt);
nt->add(ncr);
}
foreach (Element* e, seg->annotations()) {
if (e->generated() || e->systemFlag())
continue;
if (e->track() != srcTrack)
continue;
Element* ne = e->clone();
ne->setTrack(dstTrack);
seg->add(ne);
}
if (oe->type() == Element::CHORD) {
Chord* och = static_cast<Chord*>(ocr);
Chord* nch = static_cast<Chord*>(ncr);
int n = och->notes().size();
for (int i = 0; i < n; ++i) {
Note* on = och->notes().at(i);
Note* nn = nch->notes().at(i);
if (on->tieFor()) {
Tie* tie = new Tie(score);
nn->setTieFor(tie);
tie->setStartNote(nn);
tie->setTrack(nn->track());
tieMap.add(on->tieFor(), tie);
}
if (on->tieBack()) {
Tie* tie = tieMap.findNew(on->tieBack());
if (tie) {
nn->setTieBack(tie);
tie->setEndNote(nn);
}
else {
qDebug("cloneStave: cannot find tie");
}
}
}
}
}
}
}
}
}
}