LCOV - code coverage report
Current view: top level - src/engraving/dom - slur.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 25.6 % 308 79
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "slur.h"
      23              : 
      24              : #include "../editing/editspanner.h"
      25              : #include "../editing/mscoreview.h"
      26              : #include "../editing/navigation.h"
      27              : #include "../editing/transaction/transaction.h"
      28              : #include "../rendering/iscorerenderer.h"
      29              : 
      30              : #include "arpeggio.h"
      31              : #include "beam.h"
      32              : #include "chord.h"
      33              : #include "masterscore.h"
      34              : #include "measure.h"
      35              : #include "note.h"
      36              : #include "part.h"
      37              : #include "score.h"
      38              : #include "staff.h"
      39              : #include "stafftype.h"
      40              : #include "stem.h"
      41              : #include "system.h"
      42              : #include "tremolotwochord.h"
      43              : 
      44              : #include "log.h"
      45              : 
      46              : using namespace mu;
      47              : using namespace mu::engraving;
      48              : using namespace muse::draw;
      49              : 
      50              : namespace mu::engraving {
      51          533 : SlurSegment::SlurSegment(Slur* sp, ElementType type)
      52          533 :     : SlurTieSegment(type, sp)
      53              : {
      54          533 : }
      55              : 
      56           34 : SlurSegment::SlurSegment(const SlurSegment& ss)
      57           34 :     : SlurTieSegment(ss)
      58              : {
      59           34 : }
      60              : 
      61              : //---------------------------------------------------------
      62              : //   searchCR
      63              : //---------------------------------------------------------
      64              : 
      65            0 : static ChordRest* searchCR(Segment* segment, track_idx_t startTrack, track_idx_t endTrack)
      66              : {
      67            0 :     for (Segment* s = segment; s; s = s->next(SegmentType::ChordRest)) {       // restrict search to measure
      68            0 :         if (startTrack > endTrack) {
      69            0 :             for (int t = static_cast<int>(startTrack) - 1; t >= static_cast<int>(endTrack); --t) {
      70            0 :                 if (s->element(t)) {
      71            0 :                     return toChordRest(s->element(t));
      72              :                 }
      73              :             }
      74              :         } else {
      75            0 :             for (track_idx_t t = startTrack; t < endTrack; ++t) {
      76            0 :                 if (s->element(t)) {
      77            0 :                     return toChordRest(s->element(t));
      78              :                 }
      79              :             }
      80              :         }
      81              :     }
      82            0 :     return 0;
      83              : }
      84              : 
      85            0 : bool SlurSegment::isEditAllowed(EditData& ed) const
      86              : {
      87            0 :     if (SlurTieSegment::isEditAllowed(ed)) {
      88            0 :         return true;
      89              :     }
      90              : 
      91            0 :     const bool moveStart = ed.curGrip == Grip::START;
      92            0 :     const bool moveEnd = ed.curGrip == Grip::END || ed.curGrip == Grip::DRAG;
      93              : 
      94            0 :     if (!((ed.modifiers & ShiftModifier) && (isSingleType()
      95            0 :                                              || (isBeginType() && moveStart) || (isEndType() && moveEnd)))) {
      96            0 :         return false;
      97              :     }
      98              : 
      99              :     static const std::set<int> navigationKeys {
     100              :         Key_Left,
     101              :         Key_Up,
     102              :         Key_Down,
     103              :         Key_Right
     104            0 :     };
     105              : 
     106            0 :     return muse::contains(navigationKeys, ed.key);
     107              : }
     108              : 
     109              : //---------------------------------------------------------
     110              : //   edit
     111              : //    return true if event is accepted
     112              : //---------------------------------------------------------
     113              : 
     114            0 : bool SlurSegment::edit(EditData& ed)
     115              : {
     116            0 :     if (!isEditAllowed(ed)) {
     117            0 :         return false;
     118              :     }
     119              : 
     120            0 :     if (SlurTieSegment::edit(ed)) {
     121            0 :         return true;
     122              :     }
     123              : 
     124            0 :     Slur* sl = slur();
     125              : 
     126            0 :     ChordRest* cr = nullptr;
     127              :     ChordRest* e;
     128              :     ChordRest* e1;
     129            0 :     const bool start = ed.curGrip == Grip::START;
     130            0 :     if (start) {
     131            0 :         e  = sl->startCR();
     132            0 :         e1 = sl->endCR();
     133              :     } else {
     134            0 :         e  = sl->endCR();
     135            0 :         e1 = sl->startCR();
     136              :     }
     137              : 
     138            0 :     const bool altMod = ed.modifiers & AltModifier;
     139            0 :     const bool shiftMod = ed.modifiers & ShiftModifier;
     140            0 :     const bool ctrlMod = ed.modifiers & ControlModifier;
     141            0 :     const bool extendToBarLine = shiftMod && altMod;
     142            0 :     const bool isPartialSlur = sl->isIncoming() || sl->isOutgoing();
     143              : 
     144            0 :     ChordRestNavigateOptions options;
     145            0 :     options.disableOverRepeats = true;
     146              : 
     147            0 :     if (ed.key == Key_Left) {
     148            0 :         if (extendToBarLine) {
     149            0 :             const Measure* measure = e->measure();
     150            0 :             if (start) {
     151            0 :                 cr = measure->firstChordRest(e->track());
     152            0 :                 if (!cr->hasPrecedingJumpItem()) {
     153            0 :                     return false;
     154              :                 }
     155            0 :                 sl->undoSetIncoming(true);
     156            0 :             } else if (e->hasFollowingJumpItem()) {
     157            0 :                 sl->undoSetOutgoing(false);
     158              :             }
     159              :         } else {
     160            0 :             if (!start && sl->isOutgoing()) {
     161            0 :                 sl->undoSetOutgoing(false);
     162              :             } else {
     163            0 :                 if (start && sl->isIncoming()) {
     164            0 :                     sl->undoSetIncoming(false);
     165              :                 }
     166            0 :                 if (ctrlMod) {
     167            0 :                     cr = Navigation::prevMeasure(score(), cr, true);
     168              :                 } else {
     169            0 :                     cr = Navigation::prevChordRest(e, options);
     170              :                 }
     171              :             }
     172              :         }
     173            0 :     } else if (ed.key == Key_Right) {
     174            0 :         if (extendToBarLine) {
     175            0 :             const Measure* measure = e->measure();
     176            0 :             if (start && e->hasPrecedingJumpItem()) {
     177            0 :                 sl->undoSetIncoming(false);
     178            0 :             } else if (!start) {
     179            0 :                 cr = measure->lastChordRest(e->track());
     180            0 :                 if (!cr->hasFollowingJumpItem()) {
     181            0 :                     return false;
     182              :                 }
     183            0 :                 sl->undoSetOutgoing(true);
     184              :             }
     185              :         } else {
     186            0 :             if (start && sl->isIncoming()) {
     187            0 :                 sl->undoSetIncoming(false);
     188              :             } else {
     189            0 :                 if (!start && sl->isOutgoing()) {
     190            0 :                     sl->undoSetOutgoing(false);
     191              :                 }
     192            0 :                 if (ctrlMod) {
     193            0 :                     cr = Navigation::nextMeasure(score(), cr, false, true);
     194              :                 } else {
     195            0 :                     cr = Navigation::nextChordRest(e, options);
     196              :                 }
     197              :             }
     198              :         }
     199            0 :     } else if (ed.key == Key_Up) {
     200            0 :         track_idx_t startTrack = e->part()->trackRange().startTrack;
     201            0 :         track_idx_t endTrack   = e->track();
     202            0 :         cr = searchCR(e->segment(), endTrack, startTrack);
     203            0 :     } else if (ed.key == Key_Down) {
     204            0 :         track_idx_t startTrack = e->track() + 1;
     205            0 :         track_idx_t endTrack   = e->part()->trackRange().endTrack;
     206            0 :         cr = searchCR(e->segment(), startTrack, endTrack);
     207              :     } else {
     208            0 :         return false;
     209              :     }
     210            0 :     if (cr && (cr != e1 || isPartialSlur)) {
     211            0 :         if (cr->staff() != e->staff() && (cr->staffType()->isTabStaff() || e->staffType()->isTabStaff())) {
     212            0 :             return false; // Cross-staff slurs don't make sense for TAB staves
     213              :         }
     214            0 :         if (cr->staff()->isLinked(e->staff())) {
     215            0 :             return false; // Don't allow slur to cross into staff that's linked to this
     216              :         }
     217            0 :         changeAnchor(ed, cr);
     218              :     }
     219            0 :     return true;
     220              : }
     221              : 
     222              : //---------------------------------------------------------
     223              : //   changeAnchor
     224              : //---------------------------------------------------------
     225              : 
     226            0 : void SlurSegment::changeAnchor(EditData& ed, EngravingItem* element)
     227              : {
     228            0 :     Transaction& tx = masterScore()->transactionManager()->currentOrDummyTransaction();
     229              : 
     230            0 :     ChordRest* cr = element->isChordRest() ? toChordRest(element) : nullptr;
     231            0 :     ChordRest* scr = spanner()->startCR();
     232            0 :     ChordRest* ecr = spanner()->endCR();
     233            0 :     if (!cr || !scr || !ecr) {
     234            0 :         return;
     235              :     }
     236              : 
     237              :     // save current start/end elements
     238            0 :     for (EngravingObject* e : spanner()->linkList()) {
     239            0 :         Spanner* sp = toSpanner(e);
     240            0 :         tx.pushWithoutPerforming(new ChangeStartEndSpanner(sp, sp->startElement(), sp->endElement()));
     241            0 :     }
     242              : 
     243            0 :     if (ed.curGrip == Grip::START) {
     244            0 :         spanner()->undoChangeProperty(Pid::SPANNER_TICK, cr->tick());
     245            0 :         Fraction ticks = ecr->tick() - cr->tick();
     246            0 :         spanner()->undoChangeProperty(Pid::SPANNER_TICKS, ticks);
     247            0 :         int diff = static_cast<int>(cr->track() - spanner()->track());
     248            0 :         for (auto e : spanner()->linkList()) {
     249            0 :             Spanner* s = toSpanner(e);
     250            0 :             s->undoChangeProperty(Pid::TRACK, s->track() + diff);
     251            0 :         }
     252            0 :         scr = cr;
     253              :     } else {
     254            0 :         Fraction ticks = cr->tick() - scr->tick();
     255            0 :         spanner()->undoChangeProperty(Pid::SPANNER_TICKS, ticks);
     256            0 :         int diff = static_cast<int>(cr->track() - spanner()->track());
     257            0 :         for (auto e : spanner()->linkList()) {
     258            0 :             Spanner* s = toSpanner(e);
     259            0 :             s->undoChangeProperty(Pid::SPANNER_TRACK2, s->track() + diff);
     260            0 :         }
     261            0 :         ecr = cr;
     262              :     }
     263              : 
     264              :     // update start/end elements (which could be grace notes)
     265            0 :     slur()->undoChangeStartEndElements(scr, ecr);
     266              : 
     267            0 :     const size_t segments  = spanner()->spannerSegments().size();
     268            0 :     ups(ed.curGrip).off = PointF();
     269            0 :     renderer()->layoutItem(spanner());
     270            0 :     if (spanner()->spannerSegments().size() != segments) {
     271            0 :         const std::vector<SpannerSegment*>& ss = spanner()->spannerSegments();
     272            0 :         const bool moveEnd = ed.curGrip == Grip::END || ed.curGrip == Grip::DRAG;
     273            0 :         SlurSegment* newSegment = toSlurSegment(moveEnd ? ss.back() : ss.front());
     274            0 :         ed.view()->changeEditElement(newSegment);
     275            0 :         triggerLayout();
     276              :     }
     277              : }
     278              : 
     279            0 : void SlurSegment::dragGrip(EditData& ed)
     280              : {
     281            0 :     Grip g = ed.curGrip;
     282              : 
     283            0 :     switch (g) {
     284            0 :     case Grip::START:
     285              :     case Grip::END:
     286            0 :         ups(g).off += ed.delta;
     287              :         //
     288              :         // move anchor for slurs/ties
     289              :         //
     290            0 :         if ((g == Grip::START && isSingleBeginType()) || (g == Grip::END && isSingleEndType())) {
     291            0 :             Slur* slr = slur();
     292            0 :             KeyboardModifiers km = ed.modifiers;
     293            0 :             EngravingItem* e = ed.view()->elementNear(ed.pos);
     294            0 :             if (e && e->isNote()) {
     295            0 :                 Note* note = toNote(e);
     296            0 :                 Fraction tick = note->chord()->tick();
     297            0 :                 if ((g == Grip::END && tick > slr->tick()) || (g == Grip::START && tick < slr->tick2())) {
     298            0 :                     if (km != (ShiftModifier | ControlModifier)) {
     299            0 :                         Chord* c = note->chord();
     300            0 :                         ed.view()->setDropTarget(note);
     301            0 :                         if (c->part() == slr->part() && c != slr->endCR()) {
     302            0 :                             changeAnchor(ed, c);
     303              :                         }
     304              :                     }
     305              :                 }
     306              :             } else {
     307            0 :                 ed.view()->setDropTarget(0);
     308              :             }
     309              :         }
     310            0 :         renderer()->computeBezier(this);
     311            0 :         break;
     312            0 :     case Grip::BEZIER1:
     313              :     case Grip::BEZIER2:
     314            0 :         ups(g).off += ed.delta;
     315            0 :         renderer()->computeBezier(this);
     316            0 :         break;
     317            0 :     case Grip::SHOULDER:
     318            0 :         ups(g).off = PointF();
     319            0 :         renderer()->computeBezier(this, ed.delta);
     320            0 :         break;
     321            0 :     case Grip::DRAG:
     322            0 :         ups(Grip::DRAG).off = PointF();
     323            0 :         roffset() += ed.delta;
     324            0 :         break;
     325            0 :     default:
     326            0 :         UNREACHABLE;
     327              :         return;
     328              :     }
     329              : 
     330            0 :     triggerLayout();
     331            0 : }
     332              : 
     333              : //---------------------------------------------------------
     334              : //   isEdited
     335              : //---------------------------------------------------------
     336              : 
     337            0 : bool SlurSegment::isEdited() const
     338              : {
     339            0 :     for (int i = 0; i < int(Grip::GRIPS); ++i) {
     340            0 :         if (!m_ups[i].off.isNull()) {
     341            0 :             return true;
     342              :         }
     343              :     }
     344            0 :     return false;
     345              : }
     346              : 
     347         2539 : bool SlurSegment::isEndPointsEdited() const
     348              : {
     349         2539 :     return !(m_ups[int(Grip::START)].off.isNull() && m_ups[int(Grip::END)].off.isNull());
     350              : }
     351              : 
     352          924 : double SlurSegment::endWidth() const
     353              : {
     354          924 :     return style().styleAbsolute(Sid::slurEndWidth);
     355              : }
     356              : 
     357          924 : double SlurSegment::midWidth() const
     358              : {
     359          924 :     return style().styleAbsolute(Sid::slurMidWidth);
     360              : }
     361              : 
     362           29 : double SlurSegment::dottedWidth() const
     363              : {
     364           29 :     return style().styleAbsolute(Sid::slurDottedWidth);
     365              : }
     366              : 
     367            0 : Color SlurSegment::curColor(const rendering::PaintOptions& opt) const
     368              : {
     369            0 :     return EngravingItem::curColor(getProperty(Pid::VISIBLE).toBool(), getProperty(Pid::COLOR).value<Color>(), opt);
     370              : }
     371              : 
     372           39 : Slur::Slur(const Slur& s)
     373           39 :     : SlurTie(s)
     374              : {
     375           39 :     _connectedElement = s._connectedElement;
     376           39 :     _partialSpannerDirection = s._partialSpannerDirection;
     377           39 : }
     378              : 
     379              : //---------------------------------------------------------
     380              : //   Slur
     381              : //---------------------------------------------------------
     382              : 
     383          528 : Slur::Slur(DummyParentOr<EngravingItem> parent, ElementType type)
     384          528 :     : SlurTie(type, parent)
     385              : {
     386          528 : }
     387              : 
     388          953 : double Slur::scalingFactor() const
     389              : {
     390          953 :     Chord* startC = startElement() && startElement()->isChord() ? toChord(startElement()) : nullptr;
     391          953 :     Chord* endC = endElement() && endElement()->isChord() ? toChord(endElement()) : nullptr;
     392              : 
     393          953 :     if (!startC || !endC) {
     394           13 :         return 1.0;
     395              :     }
     396              : 
     397          987 :     if ((startC->isGraceBefore() && startC->parent() == endC)
     398          987 :         || (endC->isGraceAfter() && endC->parent() == startC)) {
     399           34 :         return style().styleD(Sid::graceNoteMag);
     400              :     }
     401              : 
     402          906 :     if (startC->isGrace()) {
     403           29 :         startC = toChord(startC->parent());
     404              :     }
     405          906 :     if (endC->isGrace()) {
     406           28 :         endC = toChord(endC->parent());
     407              :     }
     408              : 
     409          906 :     return 0.5 * (startC->intrinsicMag() + endC->intrinsicMag());
     410              : }
     411              : 
     412            0 : void Slur::undoSetIncoming(bool incoming)
     413              : {
     414            0 :     if (incoming == isIncoming()) {
     415            0 :         return;
     416              :     }
     417              : 
     418            0 :     undoChangeProperty(Pid::PARTIAL_SPANNER_DIRECTION, calcIncomingDirection(incoming), PropertyFlags::UNSTYLED);
     419              : }
     420              : 
     421            0 : void Slur::undoSetOutgoing(bool outgoing)
     422              : {
     423            0 :     if (outgoing == isOutgoing()) {
     424            0 :         return;
     425              :     }
     426              : 
     427            0 :     undoChangeProperty(Pid::PARTIAL_SPANNER_DIRECTION, calcOutgoingDirection(outgoing), PropertyFlags::UNSTYLED);
     428              : }
     429              : 
     430            0 : void Slur::setIncoming(bool incoming)
     431              : {
     432            0 :     if (incoming == isIncoming()) {
     433            0 :         return;
     434              :     }
     435              : 
     436            0 :     _partialSpannerDirection = calcIncomingDirection(incoming);
     437              : }
     438              : 
     439            1 : void Slur::setOutgoing(bool outgoing)
     440              : {
     441            1 :     if (outgoing == isOutgoing()) {
     442            0 :         return;
     443              :     }
     444              : 
     445            1 :     _partialSpannerDirection = calcOutgoingDirection(outgoing);
     446              : }
     447              : 
     448            0 : PartialSpannerDirection Slur::calcIncomingDirection(bool incoming)
     449              : {
     450            0 :     PartialSpannerDirection dir = PartialSpannerDirection::INCOMING;
     451            0 :     if (incoming) {
     452            0 :         SlurSegment* firstSeg = nsegments() > 0 ? frontSegment() : nullptr;
     453            0 :         if (firstSeg) {
     454            0 :             firstSeg->setSlurOffset(Grip::START, PointF(0, 0));
     455              :         }
     456            0 :         dir = _partialSpannerDirection
     457            0 :               == PartialSpannerDirection::OUTGOING ? PartialSpannerDirection::BOTH : PartialSpannerDirection::INCOMING;
     458              :     } else {
     459            0 :         dir = _partialSpannerDirection == PartialSpannerDirection::BOTH ? PartialSpannerDirection::OUTGOING : PartialSpannerDirection::NONE;
     460              :     }
     461            0 :     return dir;
     462              : }
     463              : 
     464            1 : PartialSpannerDirection Slur::calcOutgoingDirection(bool outgoing)
     465              : {
     466            1 :     PartialSpannerDirection dir = PartialSpannerDirection::OUTGOING;
     467            1 :     if (outgoing) {
     468            0 :         SlurSegment* lastSeg = nsegments() > 0 ? backSegment() : nullptr;
     469            0 :         if (lastSeg) {
     470            0 :             lastSeg->setSlurOffset(Grip::END, PointF(0, 0));
     471              :         }
     472            0 :         dir = _partialSpannerDirection
     473            0 :               == PartialSpannerDirection::INCOMING ? PartialSpannerDirection::BOTH : PartialSpannerDirection::OUTGOING;
     474              :     } else {
     475            1 :         dir = _partialSpannerDirection == PartialSpannerDirection::BOTH ? PartialSpannerDirection::INCOMING : PartialSpannerDirection::NONE;
     476              :     }
     477            1 :     return dir;
     478              : }
     479              : 
     480         1311 : bool Slur::isIncoming() const
     481              : {
     482         1311 :     return _partialSpannerDirection == PartialSpannerDirection::BOTH || _partialSpannerDirection == PartialSpannerDirection::INCOMING;
     483              : }
     484              : 
     485         1289 : bool Slur::isOutgoing() const
     486              : {
     487         1289 :     return _partialSpannerDirection == PartialSpannerDirection::BOTH || _partialSpannerDirection == PartialSpannerDirection::OUTGOING;
     488              : }
     489              : 
     490            0 : void Slur::undoChangeStartEndElements(ChordRest* scr, ChordRest* ecr)
     491              : {
     492            0 :     for (EngravingObject* lsp : linkList()) {
     493            0 :         Spanner* sp = toSpanner(lsp);
     494            0 :         if (sp == this) {
     495            0 :             score()->undo(new ChangeSpannerElements(this, scr, ecr));
     496              :         } else {
     497            0 :             EngravingItem* se = 0;
     498            0 :             EngravingItem* ee = 0;
     499            0 :             if (scr) {
     500            0 :                 std::list<EngravingObject*> sel = scr->linkList();
     501            0 :                 for (EngravingObject* lcr : sel) {
     502            0 :                     EngravingItem* le = toEngravingItem(lcr);
     503            0 :                     if (le->score() == sp->score() && le->track() == sp->track()) {
     504            0 :                         se = le;
     505            0 :                         break;
     506              :                     }
     507              :                 }
     508            0 :             }
     509            0 :             if (ecr) {
     510            0 :                 std::list<EngravingObject*> sel = ecr->linkList();
     511            0 :                 for (EngravingObject* lcr : sel) {
     512            0 :                     EngravingItem* le = toEngravingItem(lcr);
     513            0 :                     if (le->score() == sp->score() && le->track() == sp->track2()) {
     514            0 :                         ee = le;
     515            0 :                         break;
     516              :                     }
     517              :                 }
     518            0 :             }
     519            0 :             if (se && ee) {
     520            0 :                 score()->undo(new ChangeStartEndSpanner(sp, se, ee));
     521            0 :                 renderer()->layoutItem(sp);
     522              :             }
     523              :         }
     524            0 :     }
     525            0 : }
     526              : 
     527              : //---------------------------------------------------------
     528              : //   setTrack
     529              : //---------------------------------------------------------
     530              : 
     531          954 : void Slur::setTrack(track_idx_t n)
     532              : {
     533          954 :     EngravingItem::setTrack(n);
     534         1082 :     for (SpannerSegment* ss : spannerSegments()) {
     535          128 :         ss->setTrack(n);
     536              :     }
     537          954 : }
     538              : 
     539         6650 : bool Slur::isCrossStaff()
     540              : {
     541        13300 :     return startCR() && endCR()
     542        19786 :            && (startCR()->staffMove() != 0 || endCR()->staffMove() != 0
     543        13136 :                || startCR()->vStaffIdx() != endCR()->vStaffIdx());
     544              : }
     545              : 
     546         1751 : bool Slur::hasCrossBeams()
     547              : {
     548         1751 :     return (startCR() && startCR()->beam() && startCR()->beam()->cross()) || (endCR() && endCR()->beam() && endCR()->beam()->cross());
     549              : }
     550              : 
     551         5708 : PropertyValue Slur::getProperty(Pid propertyId) const
     552              : {
     553         5708 :     switch (propertyId) {
     554          303 :     case Pid::PARTIAL_SPANNER_DIRECTION:
     555          303 :         return partialSpannerDirection();
     556         5405 :     default:
     557         5405 :         return SlurTie::getProperty(propertyId);
     558              :     }
     559              : }
     560              : 
     561         4630 : PropertyValue Slur::propertyDefault(Pid id) const
     562              : {
     563         4630 :     switch (id) {
     564          303 :     case Pid::PARTIAL_SPANNER_DIRECTION:
     565          606 :         return PartialSpannerDirection::NONE;
     566         4327 :     default:
     567         4327 :         return SlurTie::propertyDefault(id);
     568              :     }
     569              : }
     570              : 
     571          184 : bool Slur::setProperty(Pid propertyId, const PropertyValue& v)
     572              : {
     573          184 :     switch (propertyId) {
     574           11 :     case Pid::PARTIAL_SPANNER_DIRECTION:
     575           11 :         setPartialSpannerDirection(v.value<PartialSpannerDirection>());
     576           11 :         break;
     577          173 :     default:
     578          173 :         return SlurTie::setProperty(propertyId, v);
     579              :     }
     580           11 :     triggerLayout();
     581           11 :     return true;
     582              : }
     583              : }
        

Generated by: LCOV version 2.0-1