Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ BaseSection {
id: warnBendsBox
width: parent.width

text: qsTrc("appshell/preferences", "Color guitar bends outside of playable range")
text: qsTrc("appshell/preferences", "Color guitar bends and hammer-ons/pull-offs outside of playable range")

navigation.name: "WarnBendBox"
navigation.panel: root.navigation
Expand Down
27 changes: 27 additions & 0 deletions src/engraving/dom/chord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,33 @@ double Chord::centerX() const
return x;
}

bool Chord::allNotesTiedToNext() const
{
Chord* tiedChord = nullptr;
for (Note* note : m_notes) {
if (!note->tieFor()) {
return false;
}

Note* endNote = note->tieFor()->endNote();
Chord* endChord = endNote ? endNote->chord() : nullptr;
if (!endChord) {
return false;
}

if (!tiedChord) {
tiedChord = endChord;
continue;
}

if (endChord != tiedChord) {
return false;
}
}

return true;
}

//---------------------------------------------------------
// processSiblings
//---------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/dom/chord.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ class Chord final : public ChordRest

StartEndSlurs& startEndSlurs() { return m_startEndSlurs; }

bool allNotesTiedToNext() const;

private:

friend class Factory;
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/dom/dom.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ set(DOM_SRC
${CMAKE_CURRENT_LIST_DIR}/harmonicmark.h
${CMAKE_CURRENT_LIST_DIR}/harmony.cpp
${CMAKE_CURRENT_LIST_DIR}/harmony.h
${CMAKE_CURRENT_LIST_DIR}/hammeronpulloff.cpp
${CMAKE_CURRENT_LIST_DIR}/hammeronpulloff.h
${CMAKE_CURRENT_LIST_DIR}/harppedaldiagram.cpp
${CMAKE_CURRENT_LIST_DIR}/harppedaldiagram.h
${CMAKE_CURRENT_LIST_DIR}/hook.cpp
Expand Down
111 changes: 106 additions & 5 deletions src/engraving/dom/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "glissando.h"
#include "guitarbend.h"
#include "hairpin.h"
#include "hammeronpulloff.h"
#include "harmony.h"
#include "harppedaldiagram.h"
#include "hook.h"
Expand Down Expand Up @@ -667,8 +668,32 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons
options.disableOverRepeats = true;
secondChordRest = nextChordRest(firstChordRest, options);

if (!secondChordRest) {
secondChordRest = firstChordRest;
if (!secondChordRest || !secondChordRest->isChord()) {
if (slurTemplate && slurTemplate->isHammerOnPullOff() && firstChordRest->isChord()) {
Note* endNote = GuitarBend::createEndNote(toChord(firstChordRest)->upNote());
if (endNote) {
secondChordRest = endNote->chord();
}
}
if (!secondChordRest) {
secondChordRest = firstChordRest;
}
} else if (secondChordRest->isChord()) {
bool firstChordRestIsTiedToSecond = firstChordRest->isChord() && toChord(firstChordRest)->allNotesTiedToNext()
&& toChord(firstChordRest)->upNote()->tieFor()->endNote()->parent() == secondChordRest;

// Follow chain of tied notes and slur until the last
while (toChord(secondChordRest)->allNotesTiedToNext()) {
secondChordRest = toChord(secondChordRest)->upNote()->tieFor()->endNote()->chord();
}

// If the first chord rest is also tied to this chain, slur to the next non-tied note
if (firstChordRestIsTiedToSecond) {
ChordRest* nextCandidate = nextChordRest(secondChordRest, options);
if (nextCandidate) {
secondChordRest = nextCandidate;
}
}
}
}

Expand All @@ -687,7 +712,7 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons
slur->setEndElement(secondChordRest);

firstChordRest->score()->undoAddElement(slur);
SlurSegment* ss = new SlurSegment(firstChordRest->score()->dummy()->system());
SlurTieSegment* ss = slur->newSlurTieSegment(firstChordRest->score()->dummy()->system());
ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
if (firstChordRest == secondChordRest && !(slur->isOutgoing() || slur->isIncoming())) {
ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->style().spatium(), 0.0));
Expand Down Expand Up @@ -2507,7 +2532,8 @@ void Score::cmdFlip()
|| e->isPedalSegment()
|| e->isLyrics()
|| e->isBreath()
|| e->isFermata()) {
|| e->isFermata()
|| e->isHammerOnPullOffText()) {
e->undoChangeProperty(Pid::AUTOPLACE, true);
// TODO: undoChangeProperty() should probably do this directly
// see https://musescore.org/en/node/281432
Expand Down Expand Up @@ -2603,6 +2629,7 @@ void Score::deleteItem(EngravingItem* el)
case ElementType::KEYSIG:
case ElementType::MEASURE_NUMBER:
case ElementType::SYSTEM_LOCK_INDICATOR:
case ElementType::HAMMER_ON_PULL_OFF_TEXT:
break;
// All other types cannot be removed if generated
default:
Expand Down Expand Up @@ -3046,6 +3073,7 @@ void Score::deleteItem(EngravingItem* el)
case ElementType::HARMONIC_MARK_SEGMENT:
case ElementType::PICK_SCRAPE_SEGMENT:
case ElementType::GUITAR_BEND_SEGMENT:
case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
{
el = toSpannerSegment(el)->spanner();
if (el->isTie()) {
Expand All @@ -3061,6 +3089,10 @@ void Score::deleteItem(EngravingItem* el)
}
break;

case ElementType::HAMMER_ON_PULL_OFF_TEXT:
undoRemoveHopoText(toHammerOnPullOffText(el));
break;

case ElementType::STEM_SLASH: // cannot delete this elements
case ElementType::HOOK:
case ElementType::GUITAR_BEND_TEXT:
Expand Down Expand Up @@ -5138,7 +5170,7 @@ void Score::cloneVoice(track_idx_t strack, track_idx_t dtrack, Segment* sf, cons

if (spanner) {
// Find and add corresponding slurs and hairpins
static const std::set<ElementType> SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAIRPIN };
static const std::set<ElementType> SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAMMER_ON_PULL_OFF, ElementType::HAIRPIN };
auto spanners = score->spannerMap().findOverlapping(start.ticks(), lTick.ticks());
for (auto i = spanners.begin(); i < spanners.end(); i++) {
Spanner* sp = i->value;
Expand Down Expand Up @@ -5996,6 +6028,7 @@ static void undoChangeNoteVisibility(Note* note, bool visible)
ElementType::NOTE,
ElementType::LYRICS,
ElementType::SLUR,
ElementType::HAMMER_ON_PULL_OFF,
ElementType::CHORD, // grace notes
ElementType::LEDGER_LINE, // temporary objects, impossible to change visibility
};
Expand Down Expand Up @@ -6284,6 +6317,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
&& et != ElementType::CHORDLINE
&& et != ElementType::LYRICS
&& et != ElementType::SLUR
&& et != ElementType::HAMMER_ON_PULL_OFF
&& et != ElementType::TIE
&& et != ElementType::NOTE
&& et != ElementType::INSTRUMENT_CHANGE
Expand Down Expand Up @@ -6355,6 +6389,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
ElementType::OTTAVA,
ElementType::TRILL,
ElementType::SLUR,
ElementType::HAMMER_ON_PULL_OFF,
ElementType::VIBRATO,
ElementType::TEXTLINE,
ElementType::PEDAL,
Expand Down Expand Up @@ -7066,6 +7101,72 @@ void Score::undoRemoveElement(EngravingItem* element, bool removeLinked)
}
}

void Score::undoRemoveHopoText(HammerOnPullOffText* hopoText)
{
Chord* startChord = hopoText->startChord();
Chord* endChord = hopoText->endChord();
IF_ASSERT_FAILED(startChord && endChord) {
return;
}

HammerOnPullOffSegment* hopoSegment = toHammerOnPullOffSegment(hopoText->parentItem());
HammerOnPullOff* hopo = hopoSegment ? hopoSegment->hammerOnPullOff() : nullptr;
IF_ASSERT_FAILED(hopo) {
return;
}

Chord* hopoStartChord = toChord(hopo->startElement());
Chord* hopoEndChord = toChord(hopo->endElement());
IF_ASSERT_FAILED(hopoStartChord && hopoEndChord) {
return;
}

if (startChord == hopoStartChord && endChord == hopoEndChord) {
undoRemoveElement(hopo);
return;
}

Fraction hopoStartTick = hopo->tick();
Fraction hopoEndTick = hopo->tick2();
Fraction hopoTextStartTick = startChord->tick();
Fraction hopoTextEndTick = endChord->tick();

bool shortenFromStart = (hopoTextStartTick - hopoStartTick) < (hopoEndTick - hopoTextEndTick);
EditData editData;
editData.curGrip = shortenFromStart ? Grip::START : Grip::END;

if (shortenFromStart) {
Fraction newStartTick = hopoTextEndTick;
Fraction newTicks = hopoEndTick - newStartTick;
hopo->undoChangeProperty(Pid::SPANNER_TICK, newStartTick);
hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
hopo->undoChangeStartEndElements(endChord, hopoEndChord);
if (startChord != hopoStartChord) {
HammerOnPullOff* newHopo = Factory::createHammerOnPullOff(score()->dummy());
newHopo->setTrack(hopo->track());
newHopo->setTick(hopoStartTick);
newHopo->setTick2(hopoTextStartTick);
newHopo->setStartElement(hopoStartChord);
newHopo->setEndElement(startChord);
score()->undoAddElement(newHopo);
}
} else {
Fraction newEndTick = hopoTextStartTick;
Fraction newTicks = newEndTick - hopoStartTick;
hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
hopo->undoChangeStartEndElements(hopoStartChord, startChord);
if (endChord != hopoEndChord) {
HammerOnPullOff* newHopo = new HammerOnPullOff(score()->dummy());
newHopo->setTrack(hopo->track());
newHopo->setTick(hopoTextEndTick);
newHopo->setTick2(hopoEndTick);
newHopo->setStartElement(endChord);
newHopo->setEndElement(hopoEndChord);
score()->undoAddElement(newHopo);
}
}
}

//---------------------------------------------------------
// undoChangeSpannerElements
//---------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion src/engraving/dom/engravingobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,8 @@ bool EngravingObject::isTextBase() const
|| type() == ElementType::MMREST_RANGE
|| type() == ElementType::STICKING
|| type() == ElementType::HARP_DIAGRAM
|| type() == ElementType::GUITAR_BEND_TEXT;
|| type() == ElementType::GUITAR_BEND_TEXT
|| type() == ElementType::HAMMER_ON_PULL_OFF_TEXT;
}

//---------------------------------------------------------
Expand Down
27 changes: 23 additions & 4 deletions src/engraving/dom/engravingobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class GuitarBendText;
class HBox;
class Hairpin;
class HairpinSegment;
class HammerOnPullOff;
class HammerOnPullOffSegment;
class HammerOnPullOffText;
class HarmonicMark;
class HarmonicMarkSegment;
class Harmony;
Expand Down Expand Up @@ -346,7 +349,6 @@ class EngravingObject
CONVERT(VBox, VBOX)
CONVERT(TBox, TBOX)
CONVERT(FBox, FBOX)
CONVERT(Slur, SLUR)
CONVERT(Glissando, GLISSANDO)
CONVERT(GlissandoSegment, GLISSANDO_SEGMENT)
CONVERT(GuitarBend, GUITAR_BEND)
Expand All @@ -370,7 +372,6 @@ class EngravingObject
CONVERT(Beam, BEAM)
CONVERT(Hook, HOOK)
CONVERT(StemSlash, STEM_SLASH)
CONVERT(SlurSegment, SLUR_SEGMENT)
CONVERT(LaissezVibSegment, LAISSEZ_VIB_SEGMENT)
CONVERT(LaissezVib, LAISSEZ_VIB)
CONVERT(PartialTieSegment, PARTIAL_TIE_SEGMENT)
Expand Down Expand Up @@ -457,6 +458,9 @@ class EngravingObject
CONVERT(TimeTickAnchor, TIME_TICK_ANCHOR)
CONVERT(Parenthesis, PARENTHESIS)
CONVERT(ShadowNote, SHADOW_NOTE)
CONVERT(HammerOnPullOff, HAMMER_ON_PULL_OFF)
CONVERT(HammerOnPullOffSegment, HAMMER_ON_PULL_OFF_SEGMENT)
CONVERT(HammerOnPullOffText, HAMMER_ON_PULL_OFF_TEXT)
#undef CONVERT

virtual bool isEngravingItem() const { return false; } // overridden in element.h
Expand Down Expand Up @@ -486,6 +490,16 @@ class EngravingObject
|| isNoteLineSegment();
}

bool isSlur() const
{
return type() == ElementType::SLUR || type() == ElementType::HAMMER_ON_PULL_OFF;
}

bool isSlurSegment() const
{
return type() == ElementType::SLUR_SEGMENT || type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT;
}

bool isLineSegment() const
{
return isGlissandoSegment()
Expand Down Expand Up @@ -626,15 +640,17 @@ static inline SlurTieSegment* toSlurTieSegment(EngravingObject* e)
{
assert(
e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT
|| e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT);
|| e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT
|| e->type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT);
return (SlurTieSegment*)e;
}

static inline const SlurTieSegment* toSlurTieSegment(const EngravingObject* e)
{
assert(
e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT
|| e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT);
|| e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT
|| e->type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT);
return (const SlurTieSegment*)e;
}

Expand Down Expand Up @@ -877,5 +893,8 @@ CONVERT(PartialLyricsLine)
CONVERT(PartialLyricsLineSegment)
CONVERT(Parenthesis)
CONVERT(ShadowNote)
CONVERT(HammerOnPullOff)
CONVERT(HammerOnPullOffSegment)
CONVERT(HammerOnPullOffText)
#undef CONVERT
}
5 changes: 5 additions & 0 deletions src/engraving/dom/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "gradualtempochange.h"
#include "guitarbend.h"
#include "hairpin.h"
#include "hammeronpulloff.h"
#include "harmonicmark.h"
#include "harmony.h"
#include "harppedaldiagram.h"
Expand Down Expand Up @@ -220,6 +221,7 @@ EngravingItem* Factory::doCreateItem(ElementType type, EngravingItem* parent)
case ElementType::FIGURED_BASS: return new FiguredBass(parent->isSegment() ? toSegment(parent) : dummy->segment());
case ElementType::STEM: return new Stem(parent->isChord() ? toChord(parent) : dummy->chord());
case ElementType::SLUR: return new Slur(parent);
case ElementType::HAMMER_ON_PULL_OFF: return new HammerOnPullOff(parent);
case ElementType::TIE: return new Tie(parent);
case ElementType::TUPLET: return new Tuplet(parent->isMeasure() ? toMeasure(parent) : dummy->measure());
case ElementType::FINGERING: return new Fingering(parent->isNote() ? toNote(parent) : dummy->note());
Expand Down Expand Up @@ -661,6 +663,9 @@ COPY_ITEM_IMPL(Tuplet)
CREATE_ITEM_IMPL(Hairpin, ElementType::HAIRPIN, EngravingItem, isAccessibleEnabled)
MAKE_ITEM_IMPL(Hairpin, EngravingItem)

CREATE_ITEM_IMPL(HammerOnPullOff, ElementType::HAMMER_ON_PULL_OFF, EngravingItem, isAccessibleEnabled)
MAKE_ITEM_IMPL(HammerOnPullOff, EngravingItem)

CREATE_ITEM_IMPL(Glissando, ElementType::GLISSANDO, EngravingItem, isAccessibleEnabled)
MAKE_ITEM_IMPL(Glissando, EngravingItem)

Expand Down
3 changes: 3 additions & 0 deletions src/engraving/dom/factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ class Factory
static Hairpin* createHairpin(EngravingItem* parent, bool isAccessibleEnabled = true);
static std::shared_ptr<Hairpin> makeHairpin(EngravingItem* parent);

static HammerOnPullOff* createHammerOnPullOff(EngravingItem* parent, bool isAccessibleEnabled = true);
static std::shared_ptr<HammerOnPullOff> makeHammerOnPullOff(EngravingItem* parent);

static Glissando* createGlissando(EngravingItem* parent, bool isAccessibleEnabled = true);
static std::shared_ptr<Glissando> makeGlissando(EngravingItem* parent);

Expand Down
Loading
Loading