4444#include " glissando.h"
4545#include " guitarbend.h"
4646#include " hairpin.h"
47+ #include " hammeronpulloff.h"
4748#include " harmony.h"
4849#include " harppedaldiagram.h"
4950#include " hook.h"
@@ -667,8 +668,32 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons
667668 options.disableOverRepeats = true ;
668669 secondChordRest = nextChordRest (firstChordRest, options);
669670
670- if (!secondChordRest) {
671- secondChordRest = firstChordRest;
671+ if (!secondChordRest || !secondChordRest->isChord ()) {
672+ if (slurTemplate && slurTemplate->isHammerOnPullOff () && firstChordRest->isChord ()) {
673+ Note* endNote = GuitarBend::createEndNote (toChord (firstChordRest)->upNote ());
674+ if (endNote) {
675+ secondChordRest = endNote->chord ();
676+ }
677+ }
678+ if (!secondChordRest) {
679+ secondChordRest = firstChordRest;
680+ }
681+ } else if (secondChordRest->isChord ()) {
682+ bool firstChordRestIsTiedToSecond = firstChordRest->isChord () && toChord (firstChordRest)->allNotesTiedToNext ()
683+ && toChord (firstChordRest)->upNote ()->tieFor ()->endNote ()->parent () == secondChordRest;
684+
685+ // Follow chain of tied notes and slur until the last
686+ while (toChord (secondChordRest)->allNotesTiedToNext ()) {
687+ secondChordRest = toChord (secondChordRest)->upNote ()->tieFor ()->endNote ()->chord ();
688+ }
689+
690+ // If the first chord rest is also tied to this chain, slur to the next non-tied note
691+ if (firstChordRestIsTiedToSecond) {
692+ ChordRest* nextCandidate = nextChordRest (secondChordRest, options);
693+ if (nextCandidate) {
694+ secondChordRest = nextCandidate;
695+ }
696+ }
672697 }
673698 }
674699
@@ -687,7 +712,7 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons
687712 slur->setEndElement (secondChordRest);
688713
689714 firstChordRest->score ()->undoAddElement (slur);
690- SlurSegment * ss = new SlurSegment (firstChordRest->score ()->dummy ()->system ());
715+ SlurTieSegment * ss = slur-> newSlurTieSegment (firstChordRest->score ()->dummy ()->system ());
691716 ss->setSpannerSegmentType (SpannerSegmentType::SINGLE);
692717 if (firstChordRest == secondChordRest && !(slur->isOutgoing () || slur->isIncoming ())) {
693718 ss->setSlurOffset (Grip::END, PointF (3.0 * firstChordRest->style ().spatium (), 0.0 ));
@@ -2507,7 +2532,8 @@ void Score::cmdFlip()
25072532 || e->isPedalSegment ()
25082533 || e->isLyrics ()
25092534 || e->isBreath ()
2510- || e->isFermata ()) {
2535+ || e->isFermata ()
2536+ || e->isHammerOnPullOffText ()) {
25112537 e->undoChangeProperty (Pid::AUTOPLACE, true );
25122538 // TODO: undoChangeProperty() should probably do this directly
25132539 // see https://musescore.org/en/node/281432
@@ -2603,6 +2629,7 @@ void Score::deleteItem(EngravingItem* el)
26032629 case ElementType::KEYSIG:
26042630 case ElementType::MEASURE_NUMBER:
26052631 case ElementType::SYSTEM_LOCK_INDICATOR:
2632+ case ElementType::HAMMER_ON_PULL_OFF_TEXT:
26062633 break ;
26072634 // All other types cannot be removed if generated
26082635 default :
@@ -3046,6 +3073,7 @@ void Score::deleteItem(EngravingItem* el)
30463073 case ElementType::HARMONIC_MARK_SEGMENT:
30473074 case ElementType::PICK_SCRAPE_SEGMENT:
30483075 case ElementType::GUITAR_BEND_SEGMENT:
3076+ case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
30493077 {
30503078 el = toSpannerSegment (el)->spanner ();
30513079 if (el->isTie ()) {
@@ -3061,6 +3089,10 @@ void Score::deleteItem(EngravingItem* el)
30613089 }
30623090 break ;
30633091
3092+ case ElementType::HAMMER_ON_PULL_OFF_TEXT:
3093+ undoRemoveHopoText (toHammerOnPullOffText (el));
3094+ break ;
3095+
30643096 case ElementType::STEM_SLASH: // cannot delete this elements
30653097 case ElementType::HOOK:
30663098 case ElementType::GUITAR_BEND_TEXT:
@@ -5138,7 +5170,7 @@ void Score::cloneVoice(track_idx_t strack, track_idx_t dtrack, Segment* sf, cons
51385170
51395171 if (spanner) {
51405172 // Find and add corresponding slurs and hairpins
5141- static const std::set<ElementType> SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAIRPIN };
5173+ static const std::set<ElementType> SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAMMER_ON_PULL_OFF, ElementType:: HAIRPIN };
51425174 auto spanners = score->spannerMap ().findOverlapping (start.ticks (), lTick.ticks ());
51435175 for (auto i = spanners.begin (); i < spanners.end (); i++) {
51445176 Spanner* sp = i->value ;
@@ -5996,6 +6028,7 @@ static void undoChangeNoteVisibility(Note* note, bool visible)
59966028 ElementType::NOTE,
59976029 ElementType::LYRICS,
59986030 ElementType::SLUR,
6031+ ElementType::HAMMER_ON_PULL_OFF,
59996032 ElementType::CHORD, // grace notes
60006033 ElementType::LEDGER_LINE, // temporary objects, impossible to change visibility
60016034 };
@@ -6284,6 +6317,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
62846317 && et != ElementType::CHORDLINE
62856318 && et != ElementType::LYRICS
62866319 && et != ElementType::SLUR
6320+ && et != ElementType::HAMMER_ON_PULL_OFF
62876321 && et != ElementType::TIE
62886322 && et != ElementType::NOTE
62896323 && et != ElementType::INSTRUMENT_CHANGE
@@ -6355,6 +6389,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
63556389 ElementType::OTTAVA,
63566390 ElementType::TRILL,
63576391 ElementType::SLUR,
6392+ ElementType::HAMMER_ON_PULL_OFF,
63586393 ElementType::VIBRATO,
63596394 ElementType::TEXTLINE,
63606395 ElementType::PEDAL,
@@ -7066,6 +7101,72 @@ void Score::undoRemoveElement(EngravingItem* element, bool removeLinked)
70667101 }
70677102}
70687103
7104+ void Score::undoRemoveHopoText (HammerOnPullOffText* hopoText)
7105+ {
7106+ Chord* startChord = hopoText->startChord ();
7107+ Chord* endChord = hopoText->endChord ();
7108+ IF_ASSERT_FAILED (startChord && endChord) {
7109+ return ;
7110+ }
7111+
7112+ HammerOnPullOffSegment* hopoSegment = toHammerOnPullOffSegment (hopoText->parentItem ());
7113+ HammerOnPullOff* hopo = hopoSegment ? hopoSegment->hammerOnPullOff () : nullptr ;
7114+ IF_ASSERT_FAILED (hopo) {
7115+ return ;
7116+ }
7117+
7118+ Chord* hopoStartChord = toChord (hopo->startElement ());
7119+ Chord* hopoEndChord = toChord (hopo->endElement ());
7120+ IF_ASSERT_FAILED (hopoStartChord && hopoEndChord) {
7121+ return ;
7122+ }
7123+
7124+ if (startChord == hopoStartChord && endChord == hopoEndChord) {
7125+ undoRemoveElement (hopo);
7126+ return ;
7127+ }
7128+
7129+ Fraction hopoStartTick = hopo->tick ();
7130+ Fraction hopoEndTick = hopo->tick2 ();
7131+ Fraction hopoTextStartTick = startChord->tick ();
7132+ Fraction hopoTextEndTick = endChord->tick ();
7133+
7134+ bool shortenFromStart = (hopoTextStartTick - hopoStartTick) < (hopoEndTick - hopoTextEndTick);
7135+ EditData editData;
7136+ editData.curGrip = shortenFromStart ? Grip::START : Grip::END;
7137+
7138+ if (shortenFromStart) {
7139+ Fraction newStartTick = hopoTextEndTick;
7140+ Fraction newTicks = hopoEndTick - newStartTick;
7141+ hopo->undoChangeProperty (Pid::SPANNER_TICK, newStartTick);
7142+ hopo->undoChangeProperty (Pid::SPANNER_TICKS, newTicks);
7143+ hopo->undoChangeStartEndElements (endChord, hopoEndChord);
7144+ if (startChord != hopoStartChord) {
7145+ HammerOnPullOff* newHopo = Factory::createHammerOnPullOff (score ()->dummy ());
7146+ newHopo->setTrack (hopo->track ());
7147+ newHopo->setTick (hopoStartTick);
7148+ newHopo->setTick2 (hopoTextStartTick);
7149+ newHopo->setStartElement (hopoStartChord);
7150+ newHopo->setEndElement (startChord);
7151+ score ()->undoAddElement (newHopo);
7152+ }
7153+ } else {
7154+ Fraction newEndTick = hopoTextStartTick;
7155+ Fraction newTicks = newEndTick - hopoStartTick;
7156+ hopo->undoChangeProperty (Pid::SPANNER_TICKS, newTicks);
7157+ hopo->undoChangeStartEndElements (hopoStartChord, startChord);
7158+ if (endChord != hopoEndChord) {
7159+ HammerOnPullOff* newHopo = new HammerOnPullOff (score ()->dummy ());
7160+ newHopo->setTrack (hopo->track ());
7161+ newHopo->setTick (hopoTextEndTick);
7162+ newHopo->setTick2 (hopoEndTick);
7163+ newHopo->setStartElement (endChord);
7164+ newHopo->setEndElement (hopoEndChord);
7165+ score ()->undoAddElement (newHopo);
7166+ }
7167+ }
7168+ }
7169+
70697170// ---------------------------------------------------------
70707171// undoChangeSpannerElements
70717172// ---------------------------------------------------------
0 commit comments