@@ -269,6 +269,10 @@ bool StaveSharingLayout::isUnison(track_idx_t prevTrack, track_idx_t nextTrack,
269269 }
270270 }
271271
272+ if (!checkSpannersForSameVoice (prevTrack, nextTrack, ctx)) {
273+ return false ;
274+ }
275+
272276 return true ;
273277}
274278
@@ -352,6 +356,10 @@ bool StaveSharingLayout::canGoToSameVoice(track_idx_t prevTrack, track_idx_t nex
352356 }
353357 }
354358
359+ if (!checkSpannersForSameVoice (prevTrack, nextTrack, ctx)) {
360+ return false ;
361+ }
362+
355363 for (Note* unisonNote : potentialUnisonNotes) {
356364 localUnisonNotes.insert (unisonNote);
357365 }
@@ -463,6 +471,47 @@ bool StaveSharingLayout::checkNoteSpannersForUnison(const Note* note1, const Not
463471 return true ;
464472}
465473
474+ bool StaveSharingLayout::checkSpannersForSameVoice (track_idx_t prevTrack, track_idx_t nextTrack, StaveSharingContext& ctx)
475+ {
476+ std::multimap<ElementType, Spanner*> spannersOnPrevTrack;
477+ std::multimap<ElementType, Spanner*> spannersOnNextTrack;
478+
479+ for (Spanner* spanner : ctx.overlappingSpanners ) {
480+ if (spanner->track () == prevTrack) {
481+ spannersOnPrevTrack.insert ({ spanner->type (), spanner });
482+ } else if (spanner->track () == nextTrack) {
483+ spannersOnNextTrack.insert ({ spanner->type (), spanner });
484+ }
485+ }
486+
487+ if (spannersOnPrevTrack.size () != spannersOnNextTrack.size ()) {
488+ return false ;
489+ }
490+
491+ for (auto [type, spanner] : spannersOnPrevTrack) {
492+ auto range = spannersOnNextTrack.equal_range (type);
493+ if (range.first == range.second ) {
494+ // No spanner of this type exists in nextTrack
495+ return false ;
496+ }
497+
498+ Spanner* matchingSpanner = nullptr ;
499+ for (auto i = range.first ; i != range.second ; ++i) {
500+ Spanner* nextSpanner = i->second ;
501+ if (nextSpanner->tick () == spanner->tick () && nextSpanner->ticks () == spanner->ticks ()) {
502+ matchingSpanner = nextSpanner;
503+ break ;
504+ }
505+ }
506+
507+ if (!matchingSpanner) {
508+ return false ;
509+ }
510+ }
511+
512+ return true ;
513+ }
514+
466515bool StaveSharingLayout::canGoToSameStave (track_idx_t prevTrack, track_idx_t nextTrack,
467516 StaveSharingContext& ctx)
468517{
@@ -580,12 +629,19 @@ void StaveSharingLayout::disconnectAll(SharedPart* p, StaveSharingContext& ctx)
580629 }
581630 }
582631 }
632+
633+ for (Spanner* spanner : ctx.overlappingSpanners ) {
634+ if (!spanner->systemFlag () && spanner->track () >= startTrack && spanner->track () < endTrack) {
635+ EngravingItem::disconnectAllOriginItems (spanner);
636+ }
637+ }
583638}
584639
585640void StaveSharingLayout::makeSharedNotation (SharedPart* p, StaveSharingContext& ctx)
586641{
587642 makeSharedChordRests (p, ctx);
588643 makeSharedAnnotations (p, ctx);
644+ makeSharedSpanners (p, ctx);
589645}
590646
591647void StaveSharingLayout::makeSharedChordRests (SharedPart* p, StaveSharingContext& ctx)
@@ -818,7 +874,65 @@ void StaveSharingLayout::makeSharedAnnotations(SharedPart* p, StaveSharingContex
818874 }
819875 }
820876
821- for (EngravingItem* sharedItem : sharedAnnotations) {
877+ manageVoicePropertyAndTrackForSharedItems (sharedAnnotations, startOriginTrack, endOriginTrack, trackMap);
878+ }
879+
880+ void StaveSharingLayout::makeSharedSpanners (SharedPart* p, StaveSharingContext& ctx)
881+ {
882+ const SharedTrackMap& trackMap = p->trackMapAtTick (ctx.sTick );
883+ track_idx_t startOriginTrack = trackMap.begin ()->first ;
884+ track_idx_t endOriginTrack = trackMap.rbegin ()->first ;
885+
886+ std::vector<EngravingItem*> sharedSpanners;
887+
888+ std::vector<Spanner*> overlappingSpanners = ctx.overlappingSpanners ; // copy because we may add
889+ for (Spanner* spanner : overlappingSpanners) {
890+ if (spanner->track () < startOriginTrack || spanner->track () > endOriginTrack) {
891+ continue ;
892+ }
893+
894+ track_idx_t originTrack = spanner->track ();
895+ IF_ASSERT_FAILED (muse::contains (trackMap, originTrack)) {
896+ continue ;
897+ }
898+
899+ track_idx_t sharedTrack = trackMap.at (originTrack);
900+
901+ Spanner* sharedSpanner = nullptr ;
902+ for (Spanner* possibleSharedSpanner : ctx.overlappingSpanners ) {
903+ if (possibleSharedSpanner->track () == sharedTrack && possibleSharedSpanner->type () == spanner->type ()
904+ && possibleSharedSpanner->tick () == spanner->tick () && possibleSharedSpanner->ticks () == spanner->ticks ()) {
905+ sharedSpanner = possibleSharedSpanner;
906+ break ;
907+ }
908+ }
909+
910+ if (!sharedSpanner) {
911+ sharedSpanner = toSpanner (spanner->clone ());
912+ sharedSpanner->setTrack (sharedTrack);
913+ sharedSpanner->setTick (spanner->tick ());
914+ sharedSpanner->setTicks (spanner->ticks ());
915+ sharedSpanner->setStartElement (nullptr );
916+ sharedSpanner->setEndElement (nullptr );
917+ sharedSpanner->reset ();
918+
919+ ctx.score ->undoAddElement (sharedSpanner);
920+ ctx.overlappingSpanners .push_back (sharedSpanner);
921+ }
922+
923+ EngravingItem::connectSharedItem (sharedSpanner, spanner);
924+
925+ sharedSpanners.push_back (sharedSpanner);
926+ }
927+
928+ manageVoicePropertyAndTrackForSharedItems (sharedSpanners, startOriginTrack, endOriginTrack, trackMap);
929+ }
930+
931+ void StaveSharingLayout::manageVoicePropertyAndTrackForSharedItems (const std::vector<EngravingItem*>& sharedItems,
932+ track_idx_t startOriginTrack, track_idx_t endOriginTrack,
933+ const SharedTrackMap& trackMap)
934+ {
935+ for (EngravingItem* sharedItem : sharedItems) {
822936 bool refersToVoice2 = track2voice (sharedItem->track ()) != 0 ;
823937 bool voice2isUsed = refersToVoice2;
824938 if (!voice2isUsed) {
@@ -928,6 +1042,12 @@ void StaveSharingLayout::cleanup(SharedPart* p, StaveSharingContext& ctx)
9281042 }
9291043 }
9301044 }
1045+
1046+ for (Spanner* spanner : ctx.overlappingSpanners ) {
1047+ if (!spanner->systemFlag () && spanner->track () >= startTrack && spanner->track () < endTrack && spanner->originItems ().empty ()) {
1048+ score->undoRemoveElement (spanner);
1049+ }
1050+ }
9311051}
9321052
9331053StaveSharingLayout::StaveSharingContext::StaveSharingContext (MeasureBase* first, MeasureBase* last, LayoutContext& ctx)
@@ -957,5 +1077,10 @@ StaveSharingLayout::StaveSharingContext::StaveSharingContext(MeasureBase* first,
9571077 sTick = crSegments.front ()->tick ();
9581078 eTick = crSegments.back ()->measure ()->endTick ();
9591079 score = crSegments.front ()->score ();
1080+
1081+ auto spanners = score->spannerMap ().findOverlapping (sTick .ticks (), eTick.ticks ());
1082+ for (auto & i : spanners) {
1083+ overlappingSpanners.push_back (i.value );
1084+ }
9601085}
9611086}
0 commit comments