1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2009-2011 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "excerpt.h"
14 #include "score.h"
15 #include "part.h"
16 #include "xml.h"
17 #include "staff.h"
18 #include "box.h"
19 #include "textframe.h"
20 #include "style.h"
21 #include "page.h"
22 #include "text.h"
23 #include "slur.h"
24 #include "tie.h"
25 #include "sig.h"
26 #include "tempo.h"
27 #include "measure.h"
28 #include "rest.h"
29 #include "stafftype.h"
30 #include "tuplet.h"
31 #include "chord.h"
32 #include "note.h"
33 #include "lyrics.h"
34 #include "segment.h"
35 #include "textline.h"
36 #include "tupletmap.h"
37 #include "tiemap.h"
38 #include "layoutbreak.h"
39 #include "harmony.h"
40 #include "beam.h"
41 #include "utils.h"
42 #include "tremolo.h"
43 #include "barline.h"
44 #include "undo.h"
45 #include "bracketItem.h"
46 
47 namespace Ms {
48 
49 //---------------------------------------------------------
50 //   Excerpt
51 //---------------------------------------------------------
52 
Excerpt(const Excerpt & ex,bool copyPartScore)53 Excerpt::Excerpt(const Excerpt& ex, bool copyPartScore)
54    : QObject(), _oscore(ex._oscore), _title(ex._title), _parts(ex._parts), _tracks(ex._tracks)
55       {
56       _partScore = (copyPartScore && ex._partScore) ? ex._partScore->clone() : nullptr;
57       }
58 
59 //---------------------------------------------------------
60 //   ~Excerpt
61 //---------------------------------------------------------
62 
~Excerpt()63 Excerpt::~Excerpt() {
64       delete _partScore;
65       }
66 
67 //---------------------------------------------------------
68 //   nstaves
69 //---------------------------------------------------------
70 
nstaves() const71 int Excerpt::nstaves() const
72       {
73       int n { 0 };
74       for (Part* p : _parts)
75             n += p->nstaves();
76       return n;
77       }
78 
79 //---------------------------------------------------------
80 //   read
81 //---------------------------------------------------------
82 
read(XmlReader & e)83 void Excerpt::read(XmlReader& e)
84       {
85       const QList<Part*>& pl = _oscore->parts();
86       QString name;
87       while (e.readNextStartElement()) {
88             const QStringRef& tag = e.name();
89             if (tag == "name")
90                   name = e.readElementText();
91             else if (tag == "title")
92                   _title = e.readElementText().trimmed();
93             else if (tag == "part") {
94                   int partIdx = e.readInt();
95                   if (partIdx < 0 || partIdx >= pl.size())
96                         qDebug("Excerpt::read: bad part index");
97                   else
98                         _parts.append(pl.at(partIdx));
99                   }
100             }
101       if (_title.isEmpty())
102             _title = name.trimmed();
103       }
104 
105 //---------------------------------------------------------
106 //   operator!=
107 //---------------------------------------------------------
108 
operator !=(const Excerpt & e) const109 bool Excerpt::operator!=(const Excerpt& e) const
110       {
111       if (e._oscore != _oscore)
112             return true;
113       if (e._title != _title)
114             return true;
115       if (e._parts != _parts)
116             return true;
117       if (e._tracks != _tracks)
118             return true;
119       return false;
120       }
121 
122 //---------------------------------------------------------
123 //   operator==
124 //---------------------------------------------------------
125 
operator ==(const Excerpt & e) const126 bool Excerpt::operator==(const Excerpt& e) const
127       {
128       if (e._oscore != _oscore)
129             return false;
130       if (e._title != _title)
131             return false;
132       if (e._parts != _parts)
133             return false;
134       if (e._tracks != _tracks)
135             return false;
136       return true;
137       }
138 
139 //---------------------------------------------------------
140 //   createExcerpt
141 //---------------------------------------------------------
142 
createExcerpt(Excerpt * excerpt)143 void Excerpt::createExcerpt(Excerpt* excerpt)
144       {
145       MasterScore* oscore = excerpt->oscore();
146       Score* score        = excerpt->partScore();
147 
148       QList<Part*>& parts = excerpt->parts();
149       QList<int> srcStaves;
150 
151       // clone layer:
152       for (int i = 0; i < 32; ++i) {
153             score->layerTags()[i] = oscore->layerTags()[i];
154             score->layerTagComments()[i] = oscore->layerTagComments()[i];
155             }
156       score->setCurrentLayer(oscore->currentLayer());
157       score->layer().clear();
158       foreach (const Layer& l, oscore->layer())
159             score->layer().append(l);
160 
161       score->setPageNumberOffset(oscore->pageNumberOffset());
162 
163       // Set instruments and create linked staves
164       for (const Part* part : parts) {
165             Part* p = new Part(score);
166             p->setInstrument(*part->instrument());
167             p->setPartName(part->partName());
168 
169             for (Staff* staff : *part->staves()) {
170                   Staff* s = new Staff(score);
171                   s->setPart(p);
172 //                  s->setStaffType(0, *staff->staffType(0));              // TODO
173                   s->init(staff);
174                   s->setDefaultClefType(staff->defaultClefType());
175                   // the order of staff - s matters as staff should be the first entry in the
176                   // created link list to make primaryStaff() work
177                   // TODO: change implementation, maybe create an explicit "primary" flag
178                   score->undo(new Link(s, staff));
179                   p->staves()->append(s);
180                   score->staves().append(s);
181                   srcStaves.append(staff->idx());
182                   }
183             score->appendPart(p);
184             }
185 
186       // Fill tracklist (map all tracks of a stave)
187       if (excerpt->tracks().isEmpty()) {
188             QMultiMap<int, int> tracks;
189             for (Staff* s : score->staves()) {
190                   const LinkedElements* ls = s->links();
191                   if (ls == 0)
192                         continue;
193                   for (auto le : *ls) {
194                         Staff* ps = toStaff(le);
195                         if (ps->primaryStaff()) {
196                               for (int i = 0; i < VOICES; i++) {
197                                     tracks.insert(ps->idx() * VOICES + i % VOICES, s->idx() * VOICES + i % VOICES);
198                                     }
199                               break;
200                               }
201                         }
202                   }
203             excerpt->setTracks(tracks);
204             }
205 
206       cloneStaves(oscore, score, srcStaves, excerpt->tracks());
207 
208       // create excerpt title and title frame for all scores if not already there
209       MeasureBase* measure = oscore->first();
210 
211       if (!measure || !measure->isVBox()) {
212             qDebug("original score has no header frame");
213             oscore->insertMeasure(ElementType::VBOX, measure);
214             measure = oscore->first();
215             }
216       VBox* titleFrameScore = toVBox(measure);
217 
218       measure = score->first();
219       Q_ASSERT(measure->isVBox());
220 
221       VBox* titleFramePart = toVBox(measure);
222       titleFramePart->copyValues(titleFrameScore);
223       QString partLabel = excerpt->title();     // parts.front()->longName();
224       if (!partLabel.isEmpty()) {
225             Text* txt = new Text(score, Tid::INSTRUMENT_EXCERPT);
226             txt->setPlainText(partLabel);
227             measure->add(txt);
228             score->setMetaTag("partName", partLabel);
229             }
230 
231       // initial layout of score
232       score->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO);
233       score->doLayout();
234 
235       // handle transposing instruments
236       if (oscore->styleB(Sid::concertPitch) != score->styleB(Sid::concertPitch)) {
237             for (const Staff* staff : score->staves()) {
238                   if (staff->staffType(Fraction(0,1))->group() == StaffGroup::PERCUSSION)
239                         continue;
240 
241                   // if this staff has no transposition, and no instrument changes, we can skip it
242                   Interval interval = staff->part()->instrument()->transpose(); //tick?
243                   if (interval.isZero() && staff->part()->instruments()->size() == 1)
244                         continue;
245                   bool flip = false;
246                   if (oscore->styleB(Sid::concertPitch)) {
247                         interval.flip();  // flip the transposition for the original instrument
248                         flip = true;      // transposeKeys() will flip transposition for each instrument change
249                         }
250 
251                   int staffIdx   = staff->idx();
252                   int startTrack = staffIdx * VOICES;
253                   int endTrack   = startTrack + VOICES;
254 
255                   Fraction endTick = Fraction(0,1);
256                   if (score->lastSegment())
257                         endTick = score->lastSegment()->tick();
258                   score->transposeKeys(staffIdx, staffIdx+1, Fraction(0,1), endTick, interval, true, flip);
259 
260                   for (auto segment = score->firstSegmentMM(SegmentType::ChordRest); segment; segment = segment->next1MM(SegmentType::ChordRest)) {
261                         interval = staff->part()->instrument(segment->tick())->transpose();
262                         if (interval.isZero())
263                               continue;
264                         if (oscore->styleB(Sid::concertPitch))
265                               interval.flip();
266 
267                         for (auto e : segment->annotations()) {
268                               if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack))
269                                     continue;
270                               Harmony* h  = toHarmony(e);
271                               int rootTpc = Ms::transposeTpc(h->rootTpc(), interval, true);
272                               int baseTpc = Ms::transposeTpc(h->baseTpc(), interval, true);
273                               // mmrests are on by default in part
274                               // if this harmony is attached to an mmrest,
275                               // be sure to transpose harmony in underlying measure as well
276                               for (ScoreElement* se : h->linkList()) {
277                                     Harmony* hh = static_cast<Harmony*>(se);
278                                     // skip links to other staves (including in other scores)
279                                     if (hh->staff() != h->staff())
280                                           continue;
281                                     score->undoTransposeHarmony(hh, rootTpc, baseTpc);
282                                     }
283                               }
284                         }
285                   }
286             }
287 
288       // update style values if spatium different for part
289       if (oscore->spatium() != score->spatium()) {
290             //score->spatiumChanged(oscore->spatium(), score->spatium());
291             score->styleChanged();
292             }
293 
294       // second layout of score
295       score->setPlaylistDirty();
296       oscore->rebuildMidiMapping();
297       oscore->updateChannel();
298 
299       score->setLayoutAll();
300       score->doLayout();
301       }
302 
303 //---------------------------------------------------------
304 //   deleteExcerpt
305 //---------------------------------------------------------
306 
deleteExcerpt(Excerpt * excerpt)307 void MasterScore::deleteExcerpt(Excerpt* excerpt)
308       {
309       Q_ASSERT(excerpt->oscore() == this);
310       Score* partScore = excerpt->partScore();
311 
312       if (!partScore) {
313             qDebug("deleteExcerpt: no partScore");
314             return;
315             }
316 
317       // unlink the staves in the excerpt
318       for (Staff* st : partScore->staves()) {
319             bool hasLinksInMaster = false;
320             if (st->links()) {
321                   for (auto le : *st->links()) {
322                         if (le->score() == this) {
323                               hasLinksInMaster = true;
324                               break;
325                               }
326                         }
327                   }
328             if (hasLinksInMaster) {
329                   int staffIdx = st->idx();
330                   // unlink the spanners
331                   for (auto i = partScore->spanner().begin(); i != partScore->spanner().cend(); ++i) {
332                         Spanner* sp = i->second;
333                         if (sp->staffIdx() == staffIdx)
334                               sp->undoUnlink();
335                         }
336                   int sTrack = staffIdx * VOICES;
337                   int eTrack = sTrack + VOICES;
338                   // unlink elements and annotation
339                   for (Segment* s = partScore->firstSegmentMM(SegmentType::All); s; s = s->next1MM()) {
340                         for (int track = eTrack - 1; track >= sTrack; --track) {
341                               Element* el = s->element(track);
342                               if (el)
343                                     el->undoUnlink();
344                               }
345                         for (Element* e : s->annotations()) {
346                               if (e->staffIdx() == staffIdx)
347                                     e->undoUnlink();
348                               }
349                         }
350                   // unlink the staff
351                   undo(new Unlink(st));
352                   }
353             }
354       undo(new RemoveExcerpt(excerpt));
355       }
356 
357 //---------------------------------------------------------
358 //   cloneSpanner
359 //---------------------------------------------------------
360 
cloneSpanner(Spanner * s,Score * score,int dstTrack,int dstTrack2)361 static void cloneSpanner(Spanner* s, Score* score, int dstTrack, int dstTrack2)
362       {
363       // don’t clone voltas for track != 0
364       if ((s->isVolta() || (s->isTextLine() && toTextLine(s)->systemFlag())) && s->track() != 0)
365             return;
366       Spanner* ns = toSpanner(s->linkedClone());
367       ns->setScore(score);
368       ns->setParent(0);
369       ns->setTrack(dstTrack);
370       ns->setTrack2(dstTrack2);
371 
372       if (ns->isSlur()) {
373 
374             // set start/end element for slur
375             ChordRest* cr1 = s->startCR();
376             ChordRest* cr2 = s->endCR();
377 
378             ns->setStartElement(0);
379             ns->setEndElement(0);
380             if (cr1 && cr1->links()) {
381                   for (ScoreElement* e : *cr1->links()) {
382                         ChordRest* cr = toChordRest(e);
383                         if (cr == cr1)
384                               continue;
385                         if ((cr->score() == score) && (cr->tick() == ns->tick()) && cr->track() == dstTrack) {
386                               ns->setStartElement(cr);
387                               break;
388                               }
389                         }
390                   }
391             if (cr2 && cr2->links()) {
392                   for (ScoreElement* e : *cr2->links()) {
393                         ChordRest* cr = toChordRest(e);
394                         if (cr == cr2)
395                               continue;
396                         if ((cr->score() == score) && (cr->tick() == ns->tick2()) && cr->track() == dstTrack2) {
397                               ns->setEndElement(cr);
398                               break;
399                               }
400                         }
401                   }
402             if (!ns->startElement())
403                   qDebug("clone Slur: no start element");
404             if (!ns->endElement())
405                   qDebug("clone Slur: no end element");
406             }
407       score->undo(new AddElement(ns));
408       }
409 
410 //---------------------------------------------------------
411 //   cloneTuplets
412 //---------------------------------------------------------
413 
cloneTuplets(ChordRest * ocr,ChordRest * ncr,Tuplet * ot,TupletMap & tupletMap,Measure * m,int track)414 static void cloneTuplets(ChordRest* ocr, ChordRest* ncr, Tuplet* ot, TupletMap& tupletMap, Measure* m, int track)
415       {
416       ot->setTrack(ocr->track());
417       Tuplet* nt = tupletMap.findNew(ot);
418       if (nt == 0) {
419             nt = toTuplet(ot->linkedClone());
420             nt->setTrack(track);
421             nt->setParent(m);
422             nt->setScore(ncr->score());
423             tupletMap.add(ot, nt);
424 
425             Tuplet* nt1 = nt;
426             while (ot->tuplet()) {
427                   Tuplet* nt2 = tupletMap.findNew(ot->tuplet());
428                   if (nt2 == 0) {
429                         nt2 = toTuplet(ot->tuplet()->linkedClone());
430                         nt2->setTrack(track);
431                         nt2->setParent(m);
432                         nt2->setScore(ncr->score());
433                         tupletMap.add(ot->tuplet(), nt2);
434                         }
435                   nt2->add(nt1);
436                   nt1->setTuplet(nt2);
437                   ot = ot->tuplet();
438                   nt1 = nt2;
439                   }
440             }
441       nt->add(ncr);
442       ncr->setTuplet(nt);
443       }
444 
445 //---------------------------------------------------------
446 //   processLinkedClone
447 //---------------------------------------------------------
448 
processLinkedClone(Element * ne,Score * score,int strack)449 void Excerpt::processLinkedClone(Element* ne, Score* score, int strack)
450       {
451       // reset offset as most likely it will not fit
452       PropertyFlags f = ne->propertyFlags(Pid::OFFSET);
453       if (f == PropertyFlags::UNSTYLED) {
454             ne->setPropertyFlags(Pid::OFFSET, PropertyFlags::STYLED);
455             ne->resetProperty(Pid::OFFSET);
456             }
457       ne->setTrack(strack == -1 ? 0 : strack);
458       ne->setScore(score);
459       }
460 
461 //---------------------------------------------------------
462 //   cloneStaves
463 //---------------------------------------------------------
464 
cloneStaves(Score * oscore,Score * score,const QList<int> & map,QMultiMap<int,int> & trackList)465 void Excerpt::cloneStaves(Score* oscore, Score* score, const QList<int>& map, QMultiMap<int, int>& trackList)
466       {
467       TieMap  tieMap;
468 
469       MeasureBaseList* nmbl = score->measures();
470       for (MeasureBase* mb = oscore->measures()->first(); mb; mb = mb->next()) {
471             MeasureBase* nmb = 0;
472             if (mb->isHBox())
473                   nmb = new HBox(score);
474             else if (mb->isVBox())
475                   nmb = new VBox(score);
476             else if (mb->isTBox()) {
477                   nmb = new TBox(score);
478                   Text* text = toTBox(mb)->text();
479                   Element* ne = text->linkedClone();
480                   ne->setScore(score);
481                   nmb->add(ne);
482                   }
483             else if (mb->isMeasure()) {
484                   Measure* m  = toMeasure(mb);
485                   Measure* nm = new Measure(score);
486                   nmb = nm;
487                   nm->setTick(m->tick());
488                   nm->setTicks(m->ticks());
489                   nm->setTimesig(m->timesig());
490 
491                   nm->setRepeatCount(m->repeatCount());
492                   nm->setRepeatStart(m->repeatStart());
493                   nm->setRepeatEnd(m->repeatEnd());
494                   nm->setRepeatJump(m->repeatJump());
495 
496                   nm->setIrregular(m->irregular());
497                   nm->setNo(m->no());
498                   nm->setNoOffset(m->noOffset());
499                   nm->setBreakMultiMeasureRest(m->breakMultiMeasureRest());
500 
501                   for (int dstStaffIdx = 0; dstStaffIdx < map.size(); ++dstStaffIdx) {
502                         nm->setStaffStemless(dstStaffIdx, m->stemless(map[dstStaffIdx]));
503                         }
504 
505 //TODO                  nm->setEndBarLineType(
506 //                     m->endBarLineType(),
507 //                     m->endBarLineGenerated(),
508 //                     m->endBarLineVisible(),
509 //                     m->endBarLineColor());
510 
511                   // Fraction ts = nm->len();
512                   int tracks = oscore->nstaves() * VOICES;
513                   for (int srcTrack = 0; srcTrack < tracks; ++srcTrack) {
514                         TupletMap tupletMap;    // tuplets cannot cross measure boundaries
515 
516                         int strack = trackList.value(srcTrack, -1);
517 
518                         Tremolo* tremolo = 0;
519                         for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) {
520                               Segment* ns = nullptr; //create segment later, on demand
521                               for (Element* e : oseg->annotations()) {
522                                     if (e->generated())
523                                           continue;
524                                     if ((e->track() == srcTrack && strack != -1) || (e->systemFlag() && srcTrack == 0)) {
525                                           Element* ne = e->linkedClone();
526                                           processLinkedClone(ne, score, strack);
527                                           if (!ns)
528                                                 ns = nm->getSegment(oseg->segmentType(), oseg->tick());
529                                           ns->add(ne);
530                                           // for chord symbols,
531                                           // re-render with new style settings
532                                           if (ne->isHarmony()) {
533                                                 Harmony* h = toHarmony(ne);
534                                                 h->render();
535                                                 }
536                                           else if (ne->isFretDiagram()) {
537                                                 Harmony* h = toHarmony(toFretDiagram(ne)->harmony());
538                                                 if (h) {
539                                                       processLinkedClone(h, score, strack);
540                                                       h->render();
541                                                       }
542                                                 }
543                                           }
544                                     }
545 
546                               //If track is not mapped skip the following
547                               if (trackList.value(srcTrack, -1) == -1)
548                                     continue;
549 
550                               //There are probably more destination tracks for the same source
551                               QList<int> t = trackList.values(srcTrack);
552 
553                               for (int track : qAsConst(t)) {
554                                     //Clone KeySig TimeSig and Clefs if voice 1 of source staff is not mapped to a track
555                                     Element* oef = oseg->element(srcTrack & ~3);
556                                     if (oef && !oef->generated() && (oef->isTimeSig() || oef->isKeySig())
557                                         && !(trackList.size() == (score->excerpt()->nstaves() * VOICES))) {
558                                           Element* ne = oef->linkedClone();
559                                           ne->setTrack(track & ~3);
560                                           ne->setScore(score);
561                                           ns = nm->getSegment(oseg->segmentType(), oseg->tick());
562                                           ns->add(ne);
563                                           }
564 
565                                     Element* oe = oseg->element(srcTrack);
566                                     int adjustedBarlineSpan = 0;
567                                     if (srcTrack % VOICES == 0 && oseg->segmentType() == SegmentType::BarLine) {
568                                           // mid-measure barline segment
569                                           // may need to clone barline from a previous staff and/or adjust span
570                                           int oIdx = srcTrack / VOICES;
571                                           if (!oe) {
572                                                 // no barline on this staff in original score,
573                                                 // but check previous staves
574                                                 for (int i = oIdx - 1; i >= 0; --i) {
575                                                       oe = oseg->element(i * VOICES);
576                                                       if (oe)
577                                                             break;
578                                                       }
579                                                 }
580                                           if (oe) {
581                                                 // barline found, now check span
582                                                 BarLine* bl = toBarLine(oe);
583                                                 int oSpan1 = bl->staff()->idx();
584                                                 int oSpan2 = oSpan1 + bl->spanStaff();
585                                                 if (oSpan1 <= oIdx && oIdx < oSpan2) {
586                                                       // this staff is within span
587                                                       // calculate adjusted span for excerpt
588                                                       int oSpan = oSpan2 - oIdx;
589                                                       adjustedBarlineSpan = qMin(oSpan, score->nstaves());
590                                                       }
591                                                 else {
592                                                       // this staff is not within span
593                                                       oe = nullptr;
594                                                       }
595                                                 }
596                                           }
597 
598                                     if (oe && !oe->generated()) {
599                                           Element* ne;
600                                           ne = oe->linkedClone();
601                                           ne->setTrack(track);
602 
603                                           if (!(ne->track() % VOICES) && ne->isRest())
604                                                 toRest(ne)->setGap(false);
605 
606                                           ne->setScore(score);
607                                           if (oe->isBarLine() && adjustedBarlineSpan) {
608                                                 BarLine* nbl = toBarLine(ne);
609                                                 nbl->setSpanStaff(adjustedBarlineSpan);
610                                                 }
611                                           else if (oe->isChordRest()) {
612                                                 ChordRest* ocr = toChordRest(oe);
613                                                 ChordRest* ncr = toChordRest(ne);
614 
615                                                 if (ocr->beam() && !ocr->beam()->empty() && ocr->beam()->elements().front() == ocr) {
616                                                       Beam* nb = ocr->beam()->clone();
617                                                       nb->clear();
618                                                       nb->setTrack(track);
619                                                       nb->setScore(score);
620                                                       nb->add(ncr);
621                                                       ncr->setBeam(nb);
622                                                       }
623 
624                                                 Tuplet* ot = ocr->tuplet();
625 
626                                                 if (ot)
627                                                       cloneTuplets(ocr, ncr, ot, tupletMap, nm, track);
628 
629                                                 if (oe->isChord()) {
630                                                       Chord* och = toChord(ocr);
631                                                       Chord* nch = toChord(ncr);
632 
633                                                       size_t n = och->notes().size();
634                                                       for (size_t i = 0; i < n; ++i) {
635                                                             Note* on = och->notes().at(i);
636                                                             Note* nn = nch->notes().at(i);
637                                                             if (on->tieFor()) {
638                                                                   Tie* tie = toTie(on->tieFor()->linkedClone());
639                                                                   tie->setScore(score);
640                                                                   nn->setTieFor(tie);
641                                                                   tie->setStartNote(nn);
642                                                                   tie->setTrack(nn->track());
643                                                                   tieMap.add(on->tieFor(), tie);
644                                                                   }
645                                                             if (on->tieBack()) {
646                                                                   Tie* tie = tieMap.findNew(on->tieBack());
647                                                                   if (tie) {
648                                                                         nn->setTieBack(tie);
649                                                                         tie->setEndNote(nn);
650                                                                         }
651                                                                   else {
652                                                                         qDebug("cloneStaves: cannot find tie");
653                                                                         }
654                                                                   }
655                                                             // add back spanners (going back from end to start spanner element
656                                                             // makes sure the 'other' spanner anchor element is already set up)
657                                                             // 'on' is the old spanner end note and 'nn' is the new spanner end note
658                                                             for (Spanner* oldSp : on->spannerBack()) {
659                                                                   if (oldSp->startElement() && oldSp->endElement() && oldSp->startElement()->track() > oldSp->endElement()->track())
660                                                                         continue;
661                                                                   Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
662                                                                   if (newStart != nullptr) {
663                                                                         Spanner* newSp = toSpanner(oldSp->linkedClone());
664                                                                         newSp->setNoteSpan(newStart, nn);
665                                                                         score->addElement(newSp);
666                                                                         }
667                                                                   else {
668                                                                         qDebug("cloneStaves: cannot find spanner start note");
669                                                                         }
670                                                                   }
671                                                             for (Spanner* oldSp : on->spannerFor()) {
672                                                                   if (oldSp->startElement() && oldSp->endElement() && oldSp->startElement()->track() <= oldSp->endElement()->track())
673                                                                         continue;
674                                                                   Note* newEnd = Spanner::endElementFromSpanner(oldSp, nn);
675                                                                   if (newEnd != nullptr) {
676                                                                         Spanner* newSp = toSpanner(oldSp->linkedClone());
677                                                                         newSp->setNoteSpan(nn, newEnd);
678                                                                         score->addElement(newSp);
679                                                                         }
680                                                                   else {
681                                                                         qDebug("cloneStaves: cannot find spanner end note");
682                                                                         }
683                                                                   }
684                                                             }
685                                                       // two note tremolo
686                                                       if (och->tremolo() && och->tremolo()->twoNotes()) {
687                                                            if (och == och->tremolo()->chord1()) {
688                                                                   if (tremolo)
689                                                                         qDebug("unconnected two note tremolo");
690                                                                   tremolo = toTremolo(och->tremolo()->linkedClone());
691                                                                   tremolo->setScore(nch->score());
692                                                                   tremolo->setParent(nch);
693                                                                   tremolo->setTrack(nch->track());
694                                                                   tremolo->setChords(nch, 0);
695                                                                   nch->setTremolo(tremolo);
696                                                                   }
697                                                             else if (och == och->tremolo()->chord2()) {
698                                                                   if (!tremolo)
699                                                                         qDebug("first note for two note tremolo missing");
700                                                                   else {
701                                                                         tremolo->setChords(tremolo->chord1(), nch);
702                                                                         nch->setTremolo(tremolo);
703                                                                         }
704                                                                   }
705                                                             else
706                                                                   qDebug("inconsistent two note tremolo");
707                                                             }
708                                                       }
709                                                 }
710                                           if (!ns)
711                                                 ns = nm->getSegment(oseg->segmentType(), oseg->tick());
712                                           ns->add(ne);
713                                           }
714 
715                                     Segment* tst = nm->segments().firstCRSegment();
716                                     if (srcTrack % VOICES && !(track % VOICES) && (!tst || (!tst->element(track)))) {
717                                           Rest* rest = new Rest(score);
718                                           rest->setTicks(nm->ticks());
719                                           rest->setDurationType(nm->ticks());
720                                           rest->setTrack(track);
721                                           Segment* segment = nm->getSegment(SegmentType::ChordRest, nm->tick());
722                                           segment->add(rest);
723                                           }
724 
725                                     }
726                               }
727                         }
728                   }
729 
730             nmb->linkTo(mb);
731             for (Element* e : mb->el()) {
732                   if (e->isLayoutBreak()) {
733                         LayoutBreak::Type st = toLayoutBreak(e)->layoutBreakType();
734                         if (st == LayoutBreak::Type::PAGE || st == LayoutBreak::Type::LINE)
735                               continue;
736                         }
737                   int track = -1;
738                   if (e->track() != -1) {
739                         // try to map track
740                         track = trackList.value(e->track(), -1);
741                         if (track == -1) {
742                               // even if track not in excerpt, we need to clone system elements
743                               if (e->systemFlag())
744                                     track = 0;
745                               else
746                                     continue;
747                               }
748                         }
749 
750                   Element* ne;
751                   // link text - title, subtitle, also repeats (eg, coda/segno)
752                   // measure numbers are not stored in this list, but they should not be cloned anyhow
753                   // layout breaks other than section were skipped above,
754                   // but section breaks do need to be cloned & linked
755                   // other measure-attached elements (?) are cloned but not linked
756                   if (e->isText() && toText(e)->tid() == Tid::INSTRUMENT_EXCERPT) {
757                         // skip part name in score
758                         continue;
759                         }
760                   else if (e->isTextBase() || e->isLayoutBreak()) {
761                         ne = e->clone();
762                         ne->setAutoplace(true);
763                         ne->linkTo(e);
764                         }
765                   else
766                         ne = e->clone();
767                   ne->setScore(score);
768                   ne->setTrack(track);
769                   nmb->add(ne);
770                   }
771             nmbl->add(nmb);
772             }
773 
774       int n = map.size();
775       for (int dstStaffIdx = 0; dstStaffIdx < n; ++dstStaffIdx) {
776             Staff* srcStaff = oscore->staff(map[dstStaffIdx]);
777             Staff* dstStaff = score->staff(dstStaffIdx);
778 
779             if (srcStaff->primaryStaff()) {
780                   int span = srcStaff->barLineSpan();
781                   int sIdx = srcStaff->idx();
782                   if (dstStaffIdx == 0 && span == 0) {
783                         // this is first staff of new score,
784                         // but it was somewhere within a barline span in the old score
785                         // so, find beginning of span
786                         for (int i = 0; i <= sIdx; ++i) {
787                               span = oscore->staff(i)->barLineSpan();
788                               if (i + span > sIdx) {
789                                     sIdx = i;
790                                     break;
791                                     }
792                               }
793                         }
794                   int eIdx = sIdx + span;
795                   for (int staffIdx = sIdx; staffIdx < eIdx; ++staffIdx) {
796                         if (!map.contains(staffIdx))
797                              --span;
798                         }
799                   if (dstStaffIdx + span > n)
800                         span = n - dstStaffIdx - 1;
801                   dstStaff->setBarLineSpan(span);
802                   int idx = 0;
803                   for (BracketItem* bi : srcStaff->brackets()) {
804                         dstStaff->setBracketType(idx, bi->bracketType());
805                         dstStaff->setBracketSpan(idx, bi->bracketSpan());
806                         ++idx;
807                         }
808                   }
809             }
810 
811       for (auto i : oscore->spanner()) {
812             Spanner* s    = i.second;
813             int dstTrack  = -1;
814             int dstTrack2 = -1;
815 
816             if (s->isVolta() || (s->isTextLine() && toTextLine(s)->systemFlag())) {
817                   //always export voltas to first staff in part
818                   dstTrack  = 0;
819                   dstTrack2 = 0;
820                   cloneSpanner(s, score, dstTrack, dstTrack2);
821                   }
822             else if (s->isHairpin()) {
823                   //always export these spanners to first voice of the destination staff
824 
825                   QList<int> track1;
826                   for (int ii = s->track(); ii < s->track() + VOICES; ii++) {
827                         track1 += trackList.values(ii);
828                         }
829 
830                   for (int track : qAsConst(track1)) {
831                         if (!(track % VOICES))
832                               cloneSpanner(s, score, track, track);
833                         }
834 
835                   }
836             else {
837                   if (trackList.value(s->track(), -1) == -1 || trackList.value(s->track2(), -1) == -1)
838                         continue;
839                   QList<int> track1 = trackList.values(s->track());
840                   QList<int> track2 = trackList.values(s->track2());
841 
842                   if (track1.length() != track2.length())
843                         continue;
844 
845                   //export other spanner if staffidx matches
846                   for (int ii = 0; ii < track1.length(); ii++) {
847                         dstTrack = track1.at(ii);
848                         dstTrack2 = track2.at(ii);
849                         cloneSpanner(s, score, dstTrack, dstTrack2);
850                         }
851                   }
852             }
853       }
854 
855 //---------------------------------------------------------
856 //   cloneStaff
857 //    staves are in same score
858 //---------------------------------------------------------
859 
cloneStaff(Staff * srcStaff,Staff * dstStaff)860 void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff)
861       {
862       Score* score = srcStaff->score();
863       TieMap tieMap;
864 
865       score->undo(new Link(dstStaff, srcStaff));
866 
867       int srcStaffIdx = srcStaff->idx();
868       int dstStaffIdx = dstStaff->idx();
869 
870       for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) {
871             int sTrack = srcStaffIdx * VOICES;
872             int eTrack = sTrack + VOICES;
873             for (int srcTrack = sTrack; srcTrack < eTrack; ++srcTrack) {
874                   TupletMap tupletMap;    // tuplets cannot cross measure boundaries
875                   int dstTrack = dstStaffIdx * VOICES + (srcTrack - sTrack);
876                   Tremolo* tremolo = 0;
877                   for (Segment* seg = m->first(); seg; seg = seg->next()) {
878                         Element* oe = seg->element(srcTrack);
879                         if (oe == 0 || oe->generated())
880                               continue;
881                         if (oe->isTimeSig())
882                               continue;
883                         Element* ne = 0;
884                         if (oe->isClef()) {
885                               // only clone clef if it matches staff group and does not exists yet
886                               Clef* clef = toClef(oe);
887                               Fraction   tick = seg->tick();
888                               if (ClefInfo::staffGroup(clef->concertClef()) == dstStaff->constStaffType(Fraction(0,1))->group()
889                                           && dstStaff->clefType(tick) != clef->clefTypeList()) {
890                                     ne = oe->clone();
891                                     }
892                               }
893                         else
894                               ne = oe->linkedClone();
895                         if (ne) {
896                               ne->setTrack(dstTrack);
897                               ne->setParent(seg);
898                               ne->setScore(score);
899                               if (ne->isChordRest()) {
900                                     ChordRest* ncr = toChordRest(ne);
901                                     if (ncr->tuplet()) {
902                                           ncr->setTuplet(0); //TODO nested tuplets
903                                           }
904                                     }
905                               score->undoAddElement(ne);
906                               }
907                         if (oe->isChordRest()) {
908                               ChordRest* ocr = toChordRest(oe);
909                               ChordRest* ncr = toChordRest(ne);
910                               Tuplet* ot     = ocr->tuplet();
911                               if (ot)
912                                     cloneTuplets(ocr, ncr, ot, tupletMap, m, dstTrack);
913 
914                               // remove lyrics from chord
915                               // since only one set of lyrics is used with linked staves
916                               foreach (Lyrics* l, ncr->lyrics()) {
917                                     if (l)
918                                           l->unlink();
919                                     }
920                               qDeleteAll(ncr->lyrics());
921                               ncr->lyrics().clear();
922 
923                               for (Element* e : seg->annotations()) {
924                                     if (!e) {
925                                           qDebug("cloneStaff: corrupted annotation found.");
926                                           continue;
927                                           }
928                                     if (e->generated() || e->systemFlag())
929                                           continue;
930                                     if (e->track() != srcTrack)
931                                           continue;
932                                     switch (e->type()) {
933                                           // exclude certain element types
934                                           // this should be same list excluded in Score::undoAddElement()
935                                           case ElementType::STAFF_TEXT:
936                                           case ElementType::SYSTEM_TEXT:
937                                           case ElementType::FRET_DIAGRAM:
938                                           case ElementType::HARMONY:
939                                           case ElementType::FIGURED_BASS:
940                                           case ElementType::DYNAMIC:
941                                           case ElementType::LYRICS:   // not normally segment-attached
942                                                 continue;
943                                           case ElementType::FERMATA:
944                                                 {
945                                                 // Fermatas are special since the belong to a segment but should
946                                                 // be created and linked on each staff.
947                                                 Element* ne1 = e->linkedClone();
948                                                 ne1->setTrack(dstTrack);
949                                                 ne1->setParent(seg);
950                                                 ne1->setScore(score);
951                                                 score->undo(new AddElement(ne1));
952                                                 continue;
953                                                 }
954                                           default:
955                                                 if (toTextLine(e)->systemFlag())
956                                                       continue;
957                                                 Element* ne1 = e->clone();
958                                                 ne1->setTrack(dstTrack);
959                                                 ne1->setParent(seg);
960                                                 ne1->setScore(score);
961                                                 score->undoAddElement(ne1);
962                                           }
963                                     }
964                               if (oe->isChord()) {
965                                     Chord* och = toChord(ocr);
966                                     Chord* nch = toChord(ncr);
967                                     size_t n = och->notes().size();
968                                     for (size_t i = 0; i < n; ++i) {
969                                           Note* on = och->notes().at(i);
970                                           Note* nn = nch->notes().at(i);
971                                           if (on->tieFor()) {
972                                                 Tie* tie = toTie(on->tieFor()->linkedClone());
973                                                 tie->setScore(score);
974                                                 nn->setTieFor(tie);
975                                                 tie->setStartNote(nn);
976                                                 tie->setTrack(nn->track());
977                                                 tieMap.add(on->tieFor(), tie);
978                                                 }
979                                           if (on->tieBack()) {
980                                                 Tie* tie = tieMap.findNew(on->tieBack());
981                                                 if (tie) {
982                                                       nn->setTieBack(tie);
983                                                       tie->setEndNote(nn);
984                                                       }
985                                                 else {
986                                                       qDebug("cloneStaff: cannot find tie");
987                                                       }
988                                                 }
989                                           // add back spanners (going back from end to start spanner element
990                                           // makes sure the 'other' spanner anchor element is already set up)
991                                           // 'on' is the old spanner end note and 'nn' is the new spanner end note
992                                           for (Spanner* oldSp : on->spannerBack()) {
993                                                 Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
994                                                 if (newStart != nullptr) {
995                                                       Spanner* newSp = toSpanner(oldSp->linkedClone());
996                                                       newSp->setNoteSpan(newStart, nn);
997                                                       score->addElement(newSp);
998                                                       }
999                                                 else {
1000                                                       qDebug("cloneStaff: cannot find spanner start note");
1001                                                       }
1002                                                 }
1003                                           }
1004                                     // two note tremolo
1005                                     if (och->tremolo() && och->tremolo()->twoNotes()) {
1006                                           if (och == och->tremolo()->chord1()) {
1007                                                 if (tremolo)
1008                                                       qDebug("unconnected two note tremolo");
1009                                                 tremolo = toTremolo(och->tremolo()->linkedClone());
1010                                                 tremolo->setScore(nch->score());
1011                                                 tremolo->setParent(nch);
1012                                                 tremolo->setTrack(nch->track());
1013                                                 tremolo->setChords(nch, 0);
1014                                                 nch->setTremolo(tremolo);
1015                                                 }
1016                                           else if (och == och->tremolo()->chord2()) {
1017                                                 if (!tremolo)
1018                                                       qDebug("first note for two note tremolo missing");
1019                                                 else {
1020                                                       tremolo->setChords(tremolo->chord1(), nch);
1021                                                       nch->setTremolo(tremolo);
1022                                                       }
1023                                                 }
1024                                           else
1025                                                 qDebug("inconsistent two note tremolo");
1026                                           }
1027                                     }
1028                               }
1029                         }
1030                   }
1031             }
1032 
1033       for (auto i : score->spanner()) {
1034             Spanner* s = i.second;
1035             int staffIdx = s->staffIdx();
1036             int dstTrack = -1;
1037             int dstTrack2 = -1;
1038             if (!(s->isVolta() || (s->isTextLine() && toTextLine(s)->systemFlag()))) {
1039                   //export other spanner if staffidx matches
1040                   if (srcStaffIdx == staffIdx) {
1041                         dstTrack = dstStaffIdx * VOICES + s->voice();
1042                         dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES);
1043                         }
1044                   }
1045             if (dstTrack == -1)
1046                   continue;
1047             cloneSpanner(s, score, dstTrack, dstTrack2);
1048             }
1049       }
1050 
1051 //---------------------------------------------------------
1052 //   cloneStaff2
1053 //    staves are potentially in different scores
1054 //---------------------------------------------------------
1055 
cloneStaff2(Staff * srcStaff,Staff * dstStaff,const Fraction & stick,const Fraction & etick)1056 void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& stick, const Fraction& etick)
1057       {
1058       Score* oscore = srcStaff->score();
1059       Score* score  = dstStaff->score();
1060 
1061       Excerpt* oex = oscore->excerpt();
1062       Excerpt* ex  = score->excerpt();
1063       QMultiMap<int, int> otracks, tracks;
1064       if (oex)
1065             otracks = oex->tracks();
1066       if (ex)
1067             tracks  = ex->tracks();
1068 
1069       Measure* m1   = oscore->tick2measure(stick);
1070       Measure* m2   = oscore->tick2measure(etick);
1071 
1072       if (m2->tick() < etick) // end of score
1073             m2 = 0;
1074 
1075       TieMap tieMap;
1076 
1077       int srcStaffIdx = srcStaff->idx();
1078       int dstStaffIdx = dstStaff->idx();
1079 
1080       int sTrack = srcStaffIdx * VOICES;
1081       int eTrack = sTrack + VOICES;
1082 
1083       QMap<int, int> map;
1084       for (int i = sTrack; i < eTrack; i++) {
1085             if (!oex && !ex)
1086                   map.insert(i, dstStaffIdx * VOICES + i % VOICES);
1087             else if (oex && !ex) {
1088                   if (otracks.key(i, -1) != -1)
1089                         map.insert(i, otracks.key(i));
1090                   }
1091             else if (!oex && ex) {
1092                   for (int j : tracks.values(i)) {
1093                         if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) {
1094                               map.insert(i, j);
1095                               break;
1096                               }
1097                         }
1098                   }
1099             else if (oex && ex) {
1100                   if (otracks.key(i, -1) != -1) {
1101                         for (int j : tracks.values(otracks.key(i))) {
1102                               if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) {
1103                                     map.insert(i, j);
1104                                     break;
1105                                     }
1106                               }
1107                         }
1108                   }
1109             }
1110 
1111 
1112       for (Measure* m = m1; m && (m != m2); m = m->nextMeasure()) {
1113             Measure* nm = score->tick2measure(m->tick());
1114             for (int srcTrack : map.keys()) {
1115                   TupletMap tupletMap;    // tuplets cannot cross measure boundaries
1116                   int dstTrack = map.value(srcTrack);
1117                   for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) {
1118                         Element* oe = oseg->element(srcTrack);
1119                         if (oe == 0 || oe->generated())
1120                               continue;
1121                         if (oe->isTimeSig())
1122                               continue;
1123                         Segment* ns = nm->getSegment(oseg->segmentType(), oseg->tick());
1124                         Element* ne = oe->linkedClone();
1125                         ne->setTrack(dstTrack);
1126                         ne->setParent(ns);
1127                         ne->setScore(score);
1128                         score->undoAddElement(ne);
1129                         if (oe->isChordRest()) {
1130                               ChordRest* ocr = toChordRest(oe);
1131                               ChordRest* ncr = toChordRest(ne);
1132                               Tuplet* ot     = ocr->tuplet();
1133                               if (ot) {
1134                                     Tuplet* nt = tupletMap.findNew(ot);
1135                                     if (nt == 0) {
1136                                           // nt = new Tuplet(*ot);
1137                                           nt = toTuplet(ot->linkedClone());
1138                                           nt->clear();
1139                                           nt->setTrack(dstTrack);
1140                                           nt->setParent(m);
1141                                           tupletMap.add(ot, nt);
1142                                           }
1143                                     ncr->setTuplet(nt);
1144                                     nt->add(ncr);
1145                                     }
1146 
1147                               for (Element* e : oseg->annotations()) {
1148                                     if (e->generated() || e->systemFlag())
1149                                           continue;
1150                                     if (e->track() != srcTrack)
1151                                           continue;
1152                                     switch (e->type()) {
1153                                           // exclude certain element types
1154                                           // this should be same list excluded in Score::undoAddElement()
1155                                           case ElementType::STAFF_TEXT:
1156                                           case ElementType::SYSTEM_TEXT:
1157                                           case ElementType::FRET_DIAGRAM:
1158                                           case ElementType::HARMONY:
1159                                           case ElementType::FIGURED_BASS:
1160                                           case ElementType::DYNAMIC:
1161                                           case ElementType::LYRICS:   // not normally segment-attached
1162                                                 continue;
1163                                           default:
1164                                                 if (toTextLine(e)->systemFlag())
1165                                                       continue;
1166                                                 Element* ne1 = e->clone();
1167                                                 ne1->setTrack(dstTrack);
1168                                                 ne1->setParent(ns);
1169                                                 ne1->setScore(score);
1170                                                 score->undoAddElement(ne1);
1171                                           }
1172                                     }
1173                               if (oe->isChord()) {
1174                                     Chord* och = toChord(ocr);
1175                                     Chord* nch = toChord(ncr);
1176                                     size_t n = och->notes().size();
1177                                     for (size_t i = 0; i < n; ++i) {
1178                                           Note* on = och->notes().at(i);
1179                                           Note* nn = nch->notes().at(i);
1180                                           if (on->tieFor()) {
1181                                                 Tie* tie = toTie(on->tieFor()->linkedClone());
1182                                                 tie->setScore(score);
1183                                                 nn->setTieFor(tie);
1184                                                 tie->setStartNote(nn);
1185                                                 tie->setTrack(nn->track());
1186                                                 tieMap.add(on->tieFor(), tie);
1187                                                 }
1188                                           if (on->tieBack()) {
1189                                                 Tie* tie = tieMap.findNew(on->tieBack());
1190                                                 if (tie) {
1191                                                       nn->setTieBack(tie);
1192                                                       tie->setEndNote(nn);
1193                                                       }
1194                                                 else {
1195                                                       qDebug("cloneStaff2: cannot find tie");
1196                                                       }
1197                                                 }
1198                                           }
1199                                     }
1200                               }
1201                         }
1202                   }
1203             }
1204 
1205       for (auto i : oscore->spanner()) {
1206             Spanner* s = i.second;
1207             if (!(s->tick() >= stick && s->tick2() < etick))
1208                   continue;
1209 
1210             int staffIdx = s->staffIdx();
1211             int dstTrack = -1;
1212             int dstTrack2 = -1;
1213             if (!(s->isVolta() || (s->isTextLine() && s->systemFlag()))) {
1214                   //export other spanner if staffidx matches
1215                   if (srcStaffIdx == staffIdx) {
1216                         dstTrack  = dstStaffIdx * VOICES + s->voice();
1217                         dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES);
1218                         }
1219                   }
1220             if (dstTrack == -1)
1221                   continue;
1222             cloneSpanner(s, score, dstTrack, dstTrack2);
1223             }
1224       }
1225 
1226 //---------------------------------------------------------
1227 //   createAllExcerpt
1228 //---------------------------------------------------------
1229 
createAllExcerpt(MasterScore * score)1230 QList<Excerpt*> Excerpt::createAllExcerpt(MasterScore *score)
1231       {
1232       QList<Excerpt*> all;
1233       for (Part* part : score->parts()) {
1234             if (part->show()) {
1235                   Excerpt* e = new Excerpt(score);
1236                   e->parts().append(part);
1237                   for (int i = part->startTrack(), j = 0; i < part->endTrack(); i++, j++) {
1238                         e->tracks().insert(i, j);
1239                         }
1240                   QString name = createName(part->partName(), all);
1241                   e->setTitle(name);
1242                   all.append(e);
1243                   }
1244             }
1245       return all;
1246       }
1247 
1248 //---------------------------------------------------------
1249 //   createName
1250 //---------------------------------------------------------
1251 
createName(const QString & partName,QList<Excerpt * > & excerptList)1252 QString Excerpt::createName(const QString& partName, QList<Excerpt*>& excerptList)
1253       {
1254       QString name = partName.simplified();
1255       int count = 0;    // no of occurrences of partName
1256 
1257       for (Excerpt* e : excerptList) {
1258             // if <partName> already exists, change <partName> to <partName 1>
1259             if (e->title().compare(name) == 0)
1260                   e->setTitle(e->title() + " 1");
1261 
1262             QRegExp rx("^(.+)\\s\\d+$");
1263             if (rx.indexIn(e->title()) > -1 && rx.cap(1) == name)
1264                   count++;
1265             }
1266 
1267       if (count > 0)
1268             name += QString(" %1").arg(count + 1);
1269 
1270       return name;
1271       }
1272 
1273 //---------------------------------------------------------
1274 //   setPartScore
1275 //---------------------------------------------------------
1276 
setPartScore(Score * s)1277 void Excerpt::setPartScore(Score* s)
1278       {
1279       _partScore = s;
1280       s->setExcerpt(this);
1281       }
1282 
1283 }
1284