LCOV - code coverage report
Current view: top level - src/engraving/dom - guitarbend.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 61.1 % 714 436
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              : 
      23              : #include "../iengravingconfiguration.h" // IWYU pragma: keep
      24              : 
      25              : #include "../editing/editdata.h"
      26              : #include "../editing/elementeditdata.h"
      27              : #include "../editing/editnote.h"
      28              : #include "../editing/navigation.h"
      29              : #include "../editing/noteinput.h"
      30              : #include "../editing/transaction/transaction.h"
      31              : #include "../editing/transpose.h"
      32              : 
      33              : #include "accidental.h"
      34              : #include "actionicon.h"
      35              : #include "chord.h"
      36              : #include "guitarbend.h"
      37              : #include "note.h"
      38              : #include "part.h"
      39              : #include "rest.h"
      40              : #include "score.h"
      41              : #include "staff.h"
      42              : #include "tie.h"
      43              : #include "utils.h"
      44              : #include "whammybar.h"
      45              : 
      46              : namespace mu::engraving {
      47              : /****************************************
      48              :  *              GuitarBend
      49              :  * **************************************/
      50              : 
      51          395 : GuitarBend::GuitarBend(EngravingObject* parent)
      52          395 :     : SLine(ElementType::GUITAR_BEND, parent, ElementFlag::MOVABLE)
      53              : {
      54          395 : }
      55              : 
      56            4 : GuitarBend::GuitarBend(const GuitarBend& g)
      57            4 :     : SLine(g)
      58              : {
      59            4 :     m_bendType = g.bendType();
      60            4 :     _bendAmountInQuarterTones = g.bendAmountInQuarterTones();
      61            4 : }
      62              : 
      63          682 : GuitarBend::~GuitarBend()
      64              : {
      65          341 :     if (m_holdLine) {
      66            2 :         delete m_holdLine;
      67              :     }
      68          682 : }
      69              : 
      70          393 : LineSegment* GuitarBend::createLineSegment()
      71              : {
      72          393 :     GuitarBendSegment* seg = new GuitarBendSegment(this);
      73          393 :     seg->setTrack(track());
      74          393 :     seg->setColor(lineColor());
      75          393 :     return seg;
      76              : }
      77              : 
      78        16448 : Note* GuitarBend::startNote() const
      79              : {
      80        16448 :     EngravingItem* startEl = startElement();
      81        16448 :     IF_ASSERT_FAILED(startEl && startEl->isNote()) {
      82            0 :         return nullptr;
      83              :     }
      84        16448 :     return toNote(startEl);
      85              : }
      86              : 
      87          162 : Note* GuitarBend::startNoteOfChain() const
      88              : {
      89          162 :     Note* startOfChain = startNote();
      90          162 :     GuitarBend* prevBend = findPrecedingBend();
      91          235 :     while (startOfChain && prevBend) {
      92           73 :         startOfChain = prevBend->startNote();
      93           73 :         prevBend = prevBend->findPrecedingBend();
      94              :     }
      95              : 
      96          162 :     return startOfChain;
      97              : }
      98              : 
      99         6259 : Note* GuitarBend::endNote() const
     100              : {
     101         6259 :     EngravingItem* endEl = endElement();
     102         6259 :     IF_ASSERT_FAILED(endEl && endEl->isNote()) {
     103            0 :         return nullptr;
     104              :     }
     105         6259 :     return toNote(endEl);
     106              : }
     107              : 
     108            0 : void GuitarBend::changeBendAmount(int endBendAmount, int startBendAmount)
     109              : {
     110            0 :     if (endBendAmount == SLACK_BEND_AMOUNT) {
     111            0 :         undoChangeProperty(Pid::GUITAR_DIVE_IS_SLACK, true);
     112            0 :         if (endNote()) {
     113            0 :             endNote()->undoChangeProperty(Pid::HEAD_GROUP, NoteHeadGroup::HEAD_CROSS);
     114              :         }
     115            0 :         return;
     116            0 :     } else if (isSlack()) {
     117            0 :         undoResetProperty(Pid::GUITAR_DIVE_IS_SLACK);
     118            0 :         if (endNote()) {
     119            0 :             endNote()->undoResetProperty(Pid::HEAD_GROUP);
     120              :         }
     121              :     }
     122              : 
     123            0 :     if (bendType() == GuitarBendType::DIP) {
     124              :         // Dips and slack have no end note so set bend amount directly
     125            0 :         undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, endBendAmount);
     126            0 :         return;
     127              :     }
     128              : 
     129            0 :     if (GuitarBend* overlapping = overlappingBendOrDive()) {
     130            0 :         int curTotalBendAmount = bendAmountInQuarterTones() + overlapping->bendAmountInQuarterTones();
     131            0 :         int newBendAmountForThis = endBendAmount - startBendAmount;
     132            0 :         if (isDive()) {
     133            0 :             undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, newBendAmountForThis);
     134              :         } else {
     135            0 :             overlapping->undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, curTotalBendAmount - newBendAmountForThis);
     136              :         }
     137            0 :         return;
     138              :     }
     139              : 
     140              :     // All other bends: set bend amount by transposing end note appropriately
     141            0 :     int pitch = endBendAmount / 2 + startNoteOfChain()->pitch();
     142            0 :     int quarterOff = endBendAmount % 2;
     143            0 :     if (pitch == startNote()->pitch() && quarterOff == 1) {
     144              :         // Because a flat second is more readable than a sharp unison
     145            0 :         pitch += 1;
     146            0 :         quarterOff = -1;
     147              :     }
     148              : 
     149            0 :     setEndNotePitch(pitch, quarterOff);
     150              : }
     151              : 
     152          237 : void GuitarBend::setEndNotePitch(int pitch, int quarterToneOffset)
     153              : {
     154          237 :     Note* note = endNote();
     155          237 :     IF_ASSERT_FAILED(note) {
     156            0 :         return;
     157              :     }
     158              : 
     159          237 :     Fraction tick = note->tick();
     160          237 :     Staff* staff = note->staff();
     161              : 
     162          237 :     Key key = staff->key(tick);
     163          237 :     Interval interval = staff->transpose(tick);
     164          237 :     interval.flip();
     165              : 
     166          237 :     int targetTpc1 = pitch2tpc(pitch, key, Prefer::NEAREST);
     167          237 :     int targetTpc2 = Transpose::transposeTpc(targetTpc1, interval, true);
     168              : 
     169          237 :     auto doChangeEndNotePitch = [&]() {
     170          237 :         EditNote::undoChangePitch(score(), note, pitch, targetTpc1, targetTpc2);
     171          237 :         Note* tiedNote = note->tieFor() ? note->tieFor()->endNote() : nullptr;
     172          261 :         while (tiedNote) {
     173           24 :             EditNote::undoChangePitch(score(), tiedNote, pitch, targetTpc1, targetTpc2);
     174           24 :             tiedNote = tiedNote->tieFor() ? tiedNote->tieFor()->endNote() : nullptr;
     175              :         }
     176          474 :     };
     177              : 
     178          237 :     Note* linkedNoteOnNotationStaff = nullptr;
     179          237 :     for (EngravingObject* linked : note->linkList()) {
     180          237 :         if (!toNote(linked)->staffType()->isTabStaff()) {
     181          237 :             linkedNoteOnNotationStaff = toNote(linked);
     182          237 :             break;
     183              :         }
     184          237 :     }
     185              : 
     186          237 :     if (linkedNoteOnNotationStaff) {
     187              :         // Manage microtonal by setting appropriate microtonal accidentals, which will propagate to TAB staff too
     188          237 :         AccidentalType accidentalType = Accidental::value2MicrotonalSubtype(tpc2alter(targetTpc1), quarterToneOffset);
     189          237 :         if (Accidental::isMicrotonal(accidentalType)) {
     190           12 :             doChangeEndNotePitch();
     191           12 :             linkedNoteOnNotationStaff->updateLine();
     192           12 :             EditNote::changeAccidental(score(), linkedNoteOnNotationStaff, accidentalType);
     193              :         } else {
     194          225 :             linkedNoteOnNotationStaff->updateLine();
     195          225 :             EditNote::changeAccidental(score(), linkedNoteOnNotationStaff, accidentalType);
     196          225 :             doChangeEndNotePitch();
     197              :         }
     198              :     } else {
     199              :         // Accidental logic doesn't work on TAB, so set cents offset directly
     200            0 :         doChangeEndNotePitch();
     201            0 :         note->undoChangeProperty(Pid::CENT_OFFSET, quarterToneOffset * 50.0);
     202              :     }
     203              : 
     204          237 :     computeBendAmount();
     205              : 
     206          237 :     triggerLayout();
     207              : }
     208              : 
     209          664 : bool GuitarBend::isReleaseBend() const
     210              : {
     211          664 :     return bendAmountInQuarterTones() < 0;
     212              : }
     213              : 
     214          212 : bool GuitarBend::isFullRelease() const
     215              : {
     216          212 :     return isReleaseBend() && totBendAmountIncludingPrecedingBends() == 0;
     217              : }
     218              : 
     219          112 : bool GuitarBend::angledPreBend() const
     220              : {
     221          112 :     Note* endN = endNote();
     222          112 :     Chord* endChord = endNote()->chord();
     223          112 :     return bendType() == GuitarBendType::PRE_BEND && endN->string() > endChord->upString();
     224              : }
     225              : 
     226        45153 : bool GuitarBend::isDive() const
     227              : {
     228        45153 :     switch (bendType()) {
     229        19676 :     case GuitarBendType::DIVE:
     230              :     case GuitarBendType::PRE_DIVE:
     231              :     case GuitarBendType::DIP:
     232              :     case GuitarBendType::SCOOP:
     233        19676 :         return true;
     234        25477 :     default:
     235        25477 :         return false;
     236              :     }
     237              : }
     238              : 
     239            0 : bool GuitarBend::isFullReleaseDive() const
     240              : {
     241            0 :     return isDive() && totBendAmountIncludingPrecedingBends() == 0;
     242              : }
     243              : 
     244          120 : void GuitarBend::fixNotesFrettingForStandardBend(Note* startNote, Note* endNote)
     245              : {
     246          120 :     Staff* curStaff =  startNote->staff();
     247          120 :     Part* curPart = startNote->originPart();
     248          120 :     if (!curStaff || !curPart) {
     249            0 :         return;
     250              :     }
     251              : 
     252          120 :     const StringData* stringData = curPart->stringData(startNote->tick(), curStaff->idx());
     253          120 :     int startFret = startNote->fret();
     254          120 :     int startString = startNote->string();
     255          120 :     if (startFret == 0) {
     256              :         // Bend can't start from empty string
     257            1 :         startString += 1;
     258            1 :         startFret = stringData->fret(startNote->pitch() + curStaff->pitchOffset(startNote->tick()), startString, curStaff);
     259            1 :         startNote->undoChangeProperty(Pid::STRING, startString);
     260            1 :         startNote->undoChangeProperty(Pid::FRET, startFret);
     261              :     }
     262              : 
     263          120 :     if (endNote->string() != startString) {
     264           73 :         endNote->undoChangeProperty(Pid::STRING, startString);
     265           73 :         int endFret = stringData->fret(endNote->pitch() + curStaff->pitchOffset(endNote->tick()), startString, curStaff);
     266           73 :         endNote->undoChangeProperty(Pid::FRET, endFret);
     267              :     }
     268              : }
     269              : 
     270            0 : Note* GuitarBend::createEndNote(Note* startNote, GuitarBendType bendType)
     271              : {
     272            0 :     track_idx_t track = startNote->track();
     273            0 :     Chord* startChord = startNote->chord();
     274            0 :     Segment* startSegment = startChord->segment();
     275              : 
     276            0 :     Segment* endSegment = startSegment->nextCR(track);
     277            0 :     if (!endSegment) {
     278            0 :         return nullptr;
     279              :     }
     280              : 
     281            0 :     EngravingItem* item = endSegment->element(track);
     282            0 :     if (!item) {
     283            0 :         return nullptr;
     284              :     }
     285              : 
     286            0 :     Score* score = startNote->score();
     287            0 :     NoteVal noteVal = startNote->noteVal();
     288            0 :     Note* endNote = nullptr;
     289              : 
     290            0 :     if (item->isRest()) {
     291            0 :         Rest* rest = toRest(item);
     292            0 :         Fraction duration = std::min(startChord->ticks(), rest->ticks());
     293              : 
     294            0 :         endSegment = score->setNoteRest(endSegment, track, noteVal, duration);
     295            0 :         Chord* endChord = endSegment ? toChord(endSegment->element(track)) : nullptr;
     296            0 :         endNote = endChord ? endChord->upNote() : nullptr;
     297            0 :     } else if (item->isChord()) {
     298            0 :         Chord* chord = toChord(item);
     299            0 :         endNote = NoteInput::addNote(score->transactionManager()->currentOrDummyTransaction(), score, chord, noteVal);
     300              :     }
     301              : 
     302            0 :     if (endNote) {
     303            0 :         endNote->transposeDiatonic(bendType == GuitarBendType::BEND ? 1 : -1, true, false);
     304              :     }
     305              : 
     306            0 :     return endNote;
     307              : }
     308              : 
     309           66 : void GuitarBend::fixNotesFrettingForGraceBend(Note* grace, Note* main)
     310              : {
     311              :     // The start grace-note of bend must be on the same string as the main note
     312           66 :     int mainString = main->string();
     313           66 :     const StringData* stringData = main->originPart()->stringData(main->tick(), main->staff()->idx());
     314           66 :     int staffPitchOffset = main->staff()->pitchOffset(main->tick());
     315           66 :     int graceFret = stringData->fret(grace->pitch() + staffPitchOffset, mainString, main->staff());
     316           66 :     if (graceFret > 0) {
     317              :         // There is valid fretting
     318           60 :         grace->undoChangeProperty(Pid::STRING, mainString);
     319           60 :         grace->undoChangeProperty(Pid::FRET, graceFret);
     320              :     } else {
     321              :         // No valid fretting on this string, so it must be played on the lower string
     322            6 :         mainString += 1;
     323            6 :         main->undoChangeProperty(Pid::STRING, mainString);
     324            6 :         int mainFret = stringData->fret(main->pitch() + staffPitchOffset, mainString, main->staff());
     325            6 :         main->undoChangeProperty(Pid::FRET, mainFret);
     326              : 
     327            6 :         grace->undoChangeProperty(Pid::STRING, mainString);
     328            6 :         graceFret = stringData->fret(grace->pitch() + staffPitchOffset, mainString, main->staff());
     329            6 :         grace->undoChangeProperty(Pid::FRET, graceFret);
     330              :     }
     331           66 : }
     332              : 
     333         5290 : PropertyValue GuitarBend::getProperty(Pid id) const
     334              : {
     335         5290 :     switch (id) {
     336          304 :     case Pid::DIRECTION:
     337          304 :         return direction();
     338          304 :     case Pid::BEND_SHOW_HOLD_LINE:
     339          304 :         return static_cast<int>(showHoldLine());
     340            0 :     case Pid::BEND_START_TIME_FACTOR:
     341            0 :         return startTimeFactor();
     342            0 :     case Pid::BEND_TARGET_TIME_FACTOR:
     343            0 :         return m_targetTimeFactor.has_value() ? m_targetTimeFactor.value() : PropertyValue();
     344            0 :     case Pid::BEND_END_TIME_FACTOR:
     345            0 :         return endTimeFactor();
     346          142 :     case Pid::GUITAR_DIVE_TAB_POS:
     347          142 :         return isDive() ? m_diveTabPos : DirectionV::AUTO;
     348            0 :     case Pid::GUITAR_BEND_AMOUNT:
     349            0 :         return bendAmountInQuarterTones();
     350          142 :     case Pid::VIBRATO_LINE_TYPE:
     351          142 :         return dipVibratoType();
     352          142 :     case Pid::GUITAR_DIVE_IS_SLACK:
     353          142 :         return isSlack();
     354         4256 :     default:
     355         4256 :         return SLine::getProperty(id);
     356              :     }
     357              : }
     358              : 
     359         1796 : bool GuitarBend::setProperty(Pid propertyId, const PropertyValue& v)
     360              : {
     361         1796 :     switch (propertyId) {
     362            0 :     case Pid::DIRECTION:
     363            0 :         setDirection(v.value<DirectionV>());
     364            0 :         break;
     365            0 :     case Pid::BEND_SHOW_HOLD_LINE:
     366            0 :         setShowHoldLine(static_cast<GuitarBendShowHoldLine>(v.toInt()));
     367            0 :         break;
     368          498 :     case Pid::BEND_START_TIME_FACTOR:
     369          498 :         setStartTimeFactor(v.toReal());
     370          498 :         break;
     371            4 :     case Pid::BEND_TARGET_TIME_FACTOR:
     372            4 :         if (v.isValid()) {
     373            4 :             setTargetTimeFactor(v.toReal());
     374              :         } else {
     375            0 :             m_targetTimeFactor = std::nullopt;
     376              :         }
     377            4 :         break;
     378          498 :     case Pid::BEND_END_TIME_FACTOR:
     379          498 :         setEndTimeFactor(v.toReal());
     380          498 :         break;
     381            0 :     case Pid::COLOR:
     382            0 :         setColor(v.value<Color>());
     383            0 :         setLineColor(v.value<Color>());
     384            0 :         break;
     385            0 :     case Pid::GUITAR_DIVE_TAB_POS:
     386              :     {
     387            0 :         IF_ASSERT_FAILED(isDive()) {
     388            0 :             return false;
     389              :         }
     390            0 :         setDiveTabPos(v.value<DirectionV>());
     391            0 :         break;
     392              :     }
     393          395 :     case Pid::GUITAR_BEND_AMOUNT:
     394          395 :         setBendAmountInQuarterTones(v.toInt());
     395          395 :         break;
     396            0 :     case Pid::VIBRATO_LINE_TYPE:
     397              :     {
     398            0 :         IF_ASSERT_FAILED(bendType() == GuitarBendType::DIP) {
     399            0 :             return false;
     400              :         }
     401            0 :         setDipVibratoType(v.value<VibratoType>());
     402            0 :         break;
     403              :     }
     404            0 :     case Pid::GUITAR_DIVE_IS_SLACK:
     405            0 :         setIsSlack(v.toBool());
     406            0 :         break;
     407          401 :     default:
     408          401 :         return SLine::setProperty(propertyId, v);
     409              :     }
     410         1395 :     triggerLayout();
     411         1395 :     return true;
     412              : }
     413              : 
     414         6870 : PropertyValue GuitarBend::propertyDefault(Pid id) const
     415              : {
     416         6870 :     switch (id) {
     417          304 :     case Pid::DIRECTION:
     418          304 :         return DirectionV::AUTO;
     419          304 :     case Pid::BEND_SHOW_HOLD_LINE:
     420          304 :         return static_cast<int>(GuitarBendShowHoldLine::AUTO);
     421          395 :     case Pid::BEND_START_TIME_FACTOR:
     422          395 :         return 0.f;
     423          395 :     case Pid::BEND_TARGET_TIME_FACTOR:
     424          395 :         if (m_bendType == GuitarBendType::DIP) {
     425            2 :             return 0.25f;
     426              :         }
     427          393 :         return {};
     428          395 :     case Pid::BEND_END_TIME_FACTOR:
     429          395 :         if (m_bendType == GuitarBendType::GRACE_NOTE_BEND) {
     430           32 :             return 0.25f;
     431              :         }
     432          363 :         if (m_bendType == GuitarBendType::SCOOP) {
     433            1 :             return 0.25f;
     434              :         }
     435          362 :         if (m_bendType == GuitarBendType::DIP) {
     436            2 :             return 0.5f;
     437              :         }
     438          360 :         return 1.f;
     439          142 :     case Pid::GUITAR_DIVE_TAB_POS:
     440          142 :         return DirectionV::AUTO;
     441          395 :     case Pid::GUITAR_BEND_AMOUNT:
     442          395 :         return m_bendType == GuitarBendType::DIP ? -2 : 0;
     443          142 :     case Pid::VIBRATO_LINE_TYPE:
     444          284 :         return VibratoType::NONE;
     445          142 :     case Pid::GUITAR_DIVE_IS_SLACK:
     446          142 :         return false;
     447         4256 :     default:
     448         4256 :         return SLine::propertyDefault(id);
     449              :     }
     450              : }
     451              : 
     452         1537 : void GuitarBend::computeBendAmount()
     453              : {
     454         1537 :     if (isSlack()) {
     455            0 :         int prevAmount = 0;
     456            0 :         if (GuitarBend* prevBend = findPrecedingBend()) {
     457            0 :             prevAmount = prevBend->totBendAmountIncludingPrecedingBends();
     458              :         }
     459            0 :         setBendAmountInQuarterTones(SLACK_BEND_AMOUNT - prevAmount);
     460            0 :         computeBendText();
     461           80 :         return;
     462              :     }
     463              : 
     464         1537 :     if (bendType() == GuitarBendType::DIP) {
     465            6 :         computeBendText();
     466            6 :         return;
     467              :     }
     468              : 
     469         1531 :     if (bendType() == GuitarBendType::SCOOP) {
     470            3 :         setBendAmountInQuarterTones(-2);
     471            3 :         computeBendText();
     472            3 :         return;
     473              :     }
     474              : 
     475         1528 :     if (bendType() == GuitarBendType::SLIGHT_BEND) {
     476           71 :         setBendAmountInQuarterTones(1);
     477           71 :         computeBendText();
     478           71 :         return;
     479              :     }
     480              : 
     481         1457 :     if (isDive() && overlappingBendOrDive()) {
     482            0 :         computeBendText();
     483            0 :         return;
     484              :     }
     485              : 
     486         1457 :     GuitarBend* prevBend = findPrecedingBend();
     487         1457 :     GuitarBend* prevSlack = prevBend && prevBend->isSlack() ? prevBend : nullptr;
     488              : 
     489         1457 :     Note* startN = prevSlack ? prevSlack->startNote() : startNote();
     490         1457 :     Note* endN = endNote();
     491              : 
     492         1457 :     int startPitch = startN->pitch();
     493         1457 :     int endPitch = endN->pitch();
     494              : 
     495         1457 :     int pitchDiffInQuarterTones = 2 * (endPitch - startPitch);
     496              : 
     497         1457 :     int startOffInQuarterTones = round(startN->centOffset() / 50);
     498         1457 :     int endOffInQuarterTones = round(endN->centOffset() / 50);
     499              : 
     500         1457 :     pitchDiffInQuarterTones += endOffInQuarterTones - startOffInQuarterTones;
     501              : 
     502         1457 :     if (prevSlack) {
     503            0 :         pitchDiffInQuarterTones -= SLACK_BEND_AMOUNT;
     504              :     }
     505              : 
     506         1457 :     GuitarBend* overlappingDive = overlappingBendOrDive();
     507         1457 :     if (overlappingDive) {
     508            0 :         pitchDiffInQuarterTones -= overlappingDive->bendAmountInQuarterTones();
     509              :     }
     510              : 
     511         1457 :     setBendAmountInQuarterTones(pitchDiffInQuarterTones);
     512              : 
     513         1457 :     computeBendText();
     514              : 
     515         1457 :     computeIsInvalidOrNeedsWarning();
     516              : }
     517              : 
     518         3289 : int GuitarBend::totBendAmountIncludingPrecedingBends() const
     519              : {
     520         3289 :     int bendAmount = bendAmountInQuarterTones();
     521         6276 :     if (bendType() == GuitarBendType::PRE_DIVE || bendType() == GuitarBendType::PRE_BEND
     522         6276 :         || bendType() == GuitarBendType::DIP || bendType() == GuitarBendType::SCOOP) {
     523          633 :         return bendAmount;
     524              :     }
     525              : 
     526         2656 :     GuitarBend* prevBend = findPrecedingBend();
     527         4012 :     while (prevBend) {
     528         1966 :         bendAmount += prevBend->bendAmountInQuarterTones();
     529         1966 :         if (prevBend->bendType() == GuitarBendType::PRE_DIVE || prevBend->bendType() == GuitarBendType::PRE_BEND) {
     530          610 :             return bendAmount;
     531              :         }
     532         1356 :         prevBend = prevBend->findPrecedingBend();
     533              :     }
     534              : 
     535         2046 :     return bendAmount;
     536              : }
     537              : 
     538         1537 : void GuitarBend::computeBendText()
     539              : {
     540         1537 :     if (isSlack()) {
     541            0 :         mutldata()->setBendDigit(String(u"slack"));
     542          313 :         return;
     543              :     }
     544              : 
     545         1537 :     if (bendType() == GuitarBendType::PRE_DIVE || bendType() == GuitarBendType::PRE_BEND) {
     546          265 :         GuitarBend* prevBend = findPrecedingBend();
     547          265 :         if (prevBend && prevBend->holdLine()) {
     548            0 :             mutldata()->setBendDigit(u"");
     549            0 :             return;
     550              :         }
     551              :     }
     552              : 
     553         1537 :     int quarters = totBendAmountIncludingPrecedingBends();
     554              : 
     555         1537 :     int fulls = quarters / 4;
     556         1537 :     int quarts = quarters % 4;
     557         1537 :     if (fulls == 0 && quarters == 0) {
     558          313 :         mutldata()->setBendDigit(String());
     559          313 :         return;
     560              :     }
     561              : 
     562         1224 :     String string = bendAmountToString(fulls, quarts, style().styleB(Sid::useFractionCharacters));
     563              : 
     564         1224 :     if (isDive()) {
     565          441 :         if (quarters > 0) {
     566          125 :             string.insert(0, u"+");
     567          316 :         } else if (quarters < 0 && fulls == 0) {
     568           19 :             string.insert(0, u"-");
     569              :         }
     570              :     }
     571              : 
     572         1224 :     if (string == u"1" && style().styleB(Sid::guitarBendUseFull)) {
     573            0 :         string = String(u"full");
     574              :     }
     575              : 
     576         1224 :     mutldata()->setBendDigit(string);
     577         1224 : }
     578              : 
     579         1457 : void GuitarBend::computeIsInvalidOrNeedsWarning()
     580              : {
     581         1457 :     int totBendAmount = totBendAmountIncludingPrecedingBends();
     582              :     static constexpr int UNPLAYABLE_THRESHOLD = 14;
     583              :     static constexpr int WARNING_THRESHOLD = 8;
     584              :     static constexpr int DIVE_MAX = 16;
     585              :     static constexpr int DIVE_MIN = -32;
     586              : 
     587         1457 :     if (isDive()) {
     588          567 :         m_isInvalid = bendAmountInQuarterTones() == 0 || totBendAmount < DIVE_MIN || totBendAmount > DIVE_MAX;
     589          567 :         m_isBorderlineUnplayable = false;
     590              :     } else {
     591          890 :         m_isInvalid = totBendAmount < 0 || totBendAmount > UNPLAYABLE_THRESHOLD || bendAmountInQuarterTones() == 0;
     592          890 :         m_isBorderlineUnplayable =  totBendAmount > WARNING_THRESHOLD && totBendAmount <= UNPLAYABLE_THRESHOLD;
     593              :     }
     594         1457 : }
     595              : 
     596         2965 : GuitarBend* GuitarBend::overlappingBendOrDive() const
     597              : {
     598         2965 :     GuitarBend* overlappingBendOrDive = isDive() ? startNote()->bendFor() : startNote()->diveFor();
     599              : 
     600         1877 :     if (overlappingBendOrDive && overlappingBendOrDive != this
     601         4842 :         && overlappingBendOrDive->endNote() == endNote()) {
     602            0 :         return overlappingBendOrDive;
     603              :     }
     604              : 
     605         2965 :     return nullptr;
     606              : }
     607              : 
     608         6137 : GuitarBend* GuitarBend::findPrecedingBend() const
     609              : {
     610         6137 :     Note* startN = startNote();
     611         6549 :     while (startN->tieBack() && startN->tieBack()->startNote()) {
     612          412 :         startN = startN->tieBack()->startNote();
     613              :     }
     614              : 
     615         2988 :     auto isValidType = [](GuitarBendType t) {
     616         2988 :         switch (t) {
     617            0 :         case GuitarBendType::SLIGHT_BEND:
     618              :         case GuitarBendType::DIP:
     619              :         case GuitarBendType::SCOOP:
     620            0 :             return false;
     621         2988 :         default:
     622         2988 :             return true;
     623              :         }
     624              :     };
     625              : 
     626              :     // If this is a bend[dive], backtrack on the chain of previous dives[bends] as if they were ties
     627         6137 :     GuitarBend* bendOrDiveBack = isDive() ? startN->bendBack() : startN->diveBack();
     628         7304 :     while (bendOrDiveBack && bendOrDiveBack->isDive() != isDive() && isValidType(bendOrDiveBack->bendType())
     629         7352 :            && !bendOrDiveBack->overlappingBendOrDive()) {
     630           48 :         startN = bendOrDiveBack->startNote();
     631           48 :         bendOrDiveBack = isDive() ? startN->bendBack() : startN->diveBack();
     632              :     }
     633              : 
     634         6137 :     GuitarBend* precedingBend = isDive() ? startN->diveBack() : startN->bendBack();
     635         6137 :     if (precedingBend && precedingBend->isDive() == isDive() && isValidType(precedingBend->bendType())) {
     636         2705 :         return precedingBend;
     637              :     }
     638              : 
     639         3432 :     if (bendType() == GuitarBendType::PRE_DIVE || bendType() == GuitarBendType::PRE_BEND) {
     640          563 :         ChordRest* prevCR = Navigation::prevChordRest(startN->chord());
     641          563 :         if (prevCR && prevCR->isRest() && isDive()) {
     642           77 :             WhammyBar* whammyBar = findOverlappingWhammyBar(prevCR->tick(), tick2());
     643           77 :             if (whammyBar) {
     644            0 :                 while (prevCR && prevCR->isRest() && prevCR->tick() > whammyBar->tick()) {
     645            0 :                     prevCR = Navigation::prevChordRest(prevCR);
     646              :                 }
     647              :             }
     648              :         }
     649              : 
     650          563 :         if (!prevCR || !prevCR->isChord()) {
     651          307 :             return nullptr;
     652              :         }
     653              : 
     654          256 :         Chord* prevChord = toChord(prevCR);
     655          455 :         for (Note* note : prevChord->notes()) {
     656          268 :             while (note->tieBack() && note->tieBack()->startNote()) {
     657            3 :                 note = note->tieBack()->startNote();
     658              :             }
     659          265 :             GuitarBend* prevBend = note->bendBack();
     660          265 :             bool isValid = false;
     661          235 :             if (prevBend && prevBend->isDive() == isDive()
     662          235 :                 && isValidType(prevBend->bendType())
     663          500 :                 && prevBend->totBendAmountIncludingPrecedingBends() == bendAmountInQuarterTones()) {
     664          150 :                 isValid = true;
     665              :             }
     666          265 :             if (isValid) {
     667          150 :                 if (bendType() == GuitarBendType::PRE_BEND) {
     668           84 :                     Note* sn = prevBend->startNote();
     669           84 :                     isValid &= sn->fret() == startNote()->fret() && sn->string() == startNote()->string();
     670              :                 }
     671          150 :                 if (isValid) {
     672           66 :                     return prevBend;
     673              :                 }
     674              :             }
     675              :         }
     676              :     }
     677              : 
     678         3059 :     return nullptr;
     679              : }
     680              : 
     681           87 : GuitarBend* GuitarBend::findFollowingPreBendOrDive() const
     682              : {
     683           87 :     Note* endN = endNote();
     684           93 :     while (endN->tieFor() && endN->tieFor()->endNote()) {
     685            6 :         endN = endN->tieFor()->endNote();
     686              :     }
     687              : 
     688           87 :     ChordRest* nextCR = Navigation::nextChordRest(endN->chord());
     689           87 :     if (isDive() && nextCR && nextCR->isRest()) {
     690            0 :         WhammyBar* whammyBar = findOverlappingWhammyBar(tick(), nextCR->endTick());
     691            0 :         if (whammyBar) {
     692            0 :             while (nextCR && nextCR->isRest() && nextCR->tick() < whammyBar->tick2()) {
     693            0 :                 nextCR = Navigation::nextChordRest(nextCR);
     694              :             }
     695              :         }
     696              :     }
     697              : 
     698           87 :     if (!nextCR || !nextCR->isChord()) {
     699           31 :         return nullptr;
     700              :     }
     701              : 
     702           56 :     Chord* nextChord = toChord(nextCR);
     703          112 :     for (Note* note : nextChord->notes()) {
     704           59 :         while (note->tieFor() && note->tieFor()->endNote()) {
     705            3 :             note = note->tieFor()->endNote();
     706              :         }
     707           56 :         GuitarBend* bend = note->bendFor();
     708           56 :         bool isValid = false;
     709           38 :         if (bend && bend->isDive() == isDive()
     710           38 :             && (bend->bendType() == GuitarBendType::PRE_DIVE || bend->bendType() == GuitarBendType::PRE_BEND)
     711           94 :             && bend->bendAmountInQuarterTones() == totBendAmountIncludingPrecedingBends()) {
     712            0 :             isValid = true;
     713              :         }
     714           56 :         if (isValid) {
     715            0 :             if (bend->bendType() == GuitarBendType::PRE_BEND) {
     716            0 :                 Note* startN = bend->startNote();
     717            0 :                 isValid &= startN->fret() == startNote()->fret() && startN->string() == startNote()->string();
     718              :             }
     719            0 :             if (isValid) {
     720            0 :                 return bend;
     721              :             }
     722              :         }
     723              :     }
     724              : 
     725           56 :     return nullptr;
     726              : }
     727              : 
     728           77 : WhammyBar* GuitarBend::findOverlappingWhammyBar(Fraction startTick, Fraction endTick) const
     729              : {
     730           77 :     const auto& spanners = score()->spannerMap().findOverlapping(startTick.ticks(), endTick.ticks());
     731           83 :     for (const auto& item : spanners) {
     732            6 :         Spanner* spanner = item.value;
     733            6 :         if (spanner->isWhammyBar() && spanner->tick() < startTick && spanner->tick2() >= endTick) {
     734            0 :             Staff* spannerStaff = spanner->staff();
     735            0 :             Staff* thisStaff = staff();
     736            0 :             if (spannerStaff == thisStaff || spannerStaff->isLinked(thisStaff)) {
     737            0 :                 return toWhammyBar(spanner);
     738              :             }
     739              :         }
     740              :     }
     741              : 
     742           77 :     return nullptr;
     743              : }
     744              : 
     745         1250 : void GuitarBend::updateHoldLine()
     746              : {
     747         1250 :     Note* startOfHold = nullptr;
     748         1250 :     Note* endOfHold = nullptr;
     749              : 
     750         1250 :     bool isDipWithVibrato = bendType() == GuitarBendType::DIP && dipVibratoType() != VibratoType::NONE;
     751         1337 :     bool canHaveHoldLine = isDipWithVibrato || (staffType()->isTabStaff() && !isFullRelease() && bendType() != GuitarBendType::SCOOP
     752           87 :                                                 && bendType() != GuitarBendType::DIP);
     753         1250 :     if (isDive()) {
     754          468 :         canHaveHoldLine &= startNote() && startNote() == startNote()->chord()->upNote();
     755              :     }
     756              : 
     757         1250 :     bool needsHoldLine = false;
     758         1250 :     if (canHaveHoldLine) {
     759           87 :         startOfHold = endNote();
     760           87 :         endOfHold = startOfHold;
     761              : 
     762           87 :         GuitarBend* followingPreDive = findFollowingPreBendOrDive();
     763           87 :         endOfHold = followingPreDive ? followingPreDive->startNote() : startOfHold;
     764              : 
     765           87 :         if (endOfHold == startOfHold) {
     766           93 :             while (endOfHold->tieFor() && endOfHold->tieFor()->endNote()) {
     767            6 :                 endOfHold = endOfHold->tieFor()->endNote();
     768              :             }
     769              :         }
     770              : 
     771           87 :         if (isDive()) {
     772            0 :             GuitarBend* bendF = endOfHold->bendFor();
     773            0 :             while (bendF && !bendF->isDive() && !bendF->overlappingBendOrDive() && bendF->endNote() != endOfHold) {
     774            0 :                 endOfHold = bendF->endNote();
     775            0 :                 bendF = endOfHold->bendFor();
     776              :             }
     777              :         } else {
     778           87 :             GuitarBend* diveF = endOfHold->diveFor();
     779           87 :             while (diveF && !diveF->overlappingBendOrDive() && diveF->endNote() != endOfHold) {
     780            0 :                 endOfHold = diveF->endNote();
     781            0 :                 diveF = endOfHold->diveFor();
     782              :             }
     783              :         }
     784              : 
     785           87 :         if (showHoldLine() == GuitarBendShowHoldLine::AUTO) {
     786           87 :             needsHoldLine = endOfHold != startOfHold;
     787              :         } else {
     788            0 :             needsHoldLine = showHoldLine() == GuitarBendShowHoldLine::SHOW;
     789              :         }
     790           87 :         needsHoldLine |= isDipWithVibrato;
     791              :     }
     792              : 
     793         1250 :     if (!needsHoldLine) {
     794         1244 :         if (m_holdLine) {
     795            1 :             if (!m_holdLine->segmentsEmpty()) {
     796            0 :                 m_holdLine->eraseSpannerSegments();
     797              :             }
     798            1 :             delete m_holdLine;
     799            1 :             m_holdLine = nullptr;
     800              :         }
     801         1244 :         return;
     802              :     }
     803              : 
     804            6 :     if (endOfHold == startOfHold) {
     805            0 :         Chord* guessedEndChord = startOfHold->chord()->next();
     806            0 :         if (guessedEndChord) {
     807            0 :             for (Note* note : guessedEndChord->notes()) {
     808            0 :                 if (note->isPreBendOrDiveStart()) {
     809            0 :                     endOfHold = note;
     810            0 :                     break;
     811              :                 }
     812              :             }
     813              :         }
     814              :     }
     815              : 
     816            6 :     if (!m_holdLine) {
     817            0 :         m_holdLine = new GuitarBendHold(this);
     818              :     }
     819              : 
     820            6 :     m_holdLine->setStartElement(startOfHold);
     821            6 :     m_holdLine->setEndElement(endOfHold);
     822            6 :     m_holdLine->setTick(startOfHold->tick());
     823            6 :     m_holdLine->setTick2(endOfHold->tick());
     824            6 :     m_holdLine->setTrack(track());
     825            6 :     m_holdLine->setTrack2(track());
     826            6 :     m_holdLine->setGenerated(true);
     827            6 :     m_holdLine->setOwnershipParent(this);
     828              : }
     829              : 
     830          164 : double GuitarBend::lineWidth() const
     831              : {
     832          164 :     if (isDive()) {
     833            0 :         return (staffType() && staffType()->isTabStaff())
     834            0 :                ? style().styleAbsolute(Sid::guitarDiveLineWidthTab)
     835            0 :                : style().styleAbsolute(Sid::guitarDiveLineWidth);
     836              :     }
     837              : 
     838          328 :     return (staffType() && staffType()->isTabStaff())
     839          328 :            ? style().styleAbsolute(Sid::guitarBendLineWidthTab)
     840          164 :            : style().styleAbsolute(Sid::guitarBendLineWidth);
     841              : }
     842              : 
     843              : /****************************************
     844              :  *              GuitarBendSegment
     845              :  * **************************************/
     846              : 
     847          393 : GuitarBendSegment::GuitarBendSegment(GuitarBend* sp)
     848          393 :     : LineSegment(ElementType::GUITAR_BEND_SEGMENT, sp, ElementFlag::MOVABLE)
     849              : {
     850          393 :     m_text = new GuitarBendText(this);
     851          393 :     setFlag(ElementFlag::ON_STAFF, true);
     852          393 : }
     853              : 
     854            4 : GuitarBendSegment::GuitarBendSegment(const GuitarBendSegment& s)
     855            4 :     : LineSegment(s)
     856              : {
     857            4 :     m_vertexPointOff = s.m_vertexPointOff;
     858            4 :     m_text = new GuitarBendText(this);
     859            4 : }
     860              : 
     861          682 : GuitarBendSegment::~GuitarBendSegment()
     862              : {
     863          341 :     delete m_text;
     864          682 : }
     865              : 
     866            0 : int GuitarBendSegment::gripsCount() const
     867              : {
     868            0 :     switch (guitarBend()->bendType()) {
     869            0 :     case GuitarBendType::DIP:
     870              :     case GuitarBendType::SCOOP:
     871            0 :         return 0;
     872            0 :     case GuitarBendType::DIVE:
     873              :     case GuitarBendType::PRE_DIVE:
     874            0 :         return 3;
     875            0 :     default:
     876            0 :         return 4;
     877              :     }
     878              : }
     879              : 
     880            0 : std::vector<PointF> GuitarBendSegment::gripsPositions(const EditData&) const
     881              : {
     882            0 :     int gripCount = gripsCount();
     883            0 :     if (gripCount == 0) {
     884            0 :         return {};
     885              :     }
     886              : 
     887            0 :     std::vector<PointF> grips(gripsCount());
     888            0 :     PointF pp(pagePos());
     889            0 :     grips[int(Grip::START)] = pp;
     890            0 :     grips[int(Grip::END)] = pos2() + pp;
     891            0 :     grips[int(Grip::MIDDLE)] = pos2() * .5 + pp;
     892              : 
     893            0 :     if (gripCount > 3) {
     894            0 :         grips[int(Grip::APERTURE)] = ldata()->vertexPoint() + vertexPointOff() + pp;
     895              :     }
     896              : 
     897            0 :     return grips;
     898            0 : }
     899              : 
     900            0 : void GuitarBendSegment::startDragGrip(EditData& ed)
     901              : {
     902            0 :     ElementEditDataPtr eed = ed.getData(this);
     903            0 :     if (!eed) {
     904            0 :         return;
     905              :     }
     906            0 :     eed->pushProperty(Pid::BEND_VERTEX_OFF);
     907            0 :     LineSegment::startDragGrip(ed);
     908            0 : }
     909              : 
     910            0 : void GuitarBendSegment::dragGrip(EditData& ed)
     911              : {
     912            0 :     PointF delta = ed.evtDelta;
     913              : 
     914            0 :     switch (ed.curGrip) {
     915            0 :     case Grip::START:
     916            0 :         setOffset(offset() + delta);
     917            0 :         setUserOff2(userOff2() - delta);
     918            0 :         setVertexPointOff(vertexPointOff() - delta);
     919            0 :         break;
     920            0 :     case Grip::END:
     921            0 :         setUserOff2(userOff2() + delta);
     922            0 :         break;
     923            0 :     case Grip::APERTURE:
     924            0 :         setVertexPointOff(vertexPointOff() + delta);
     925            0 :         break;
     926            0 :     case Grip::MIDDLE:
     927            0 :         setOffset(offset() + delta);
     928            0 :         break;
     929            0 :     default:
     930            0 :         UNREACHABLE;
     931              :         break;
     932              :     }
     933              : 
     934            0 :     triggerLayout();
     935            0 : }
     936              : 
     937          304 : PropertyValue GuitarBendSegment::getProperty(Pid id) const
     938              : {
     939          304 :     switch (id) {
     940            0 :     case Pid::BEND_VERTEX_OFF:
     941            0 :         return vertexPointOff();
     942          304 :     default:
     943          304 :         return LineSegment::getProperty(id);
     944              :     }
     945              : }
     946              : 
     947          401 : bool GuitarBendSegment::setProperty(Pid propertyId, const PropertyValue& v)
     948              : {
     949          401 :     switch (propertyId) {
     950            0 :     case Pid::BEND_VERTEX_OFF:
     951            0 :         setVertexPointOff(v.value<PointF>());
     952            0 :         break;
     953          401 :     default:
     954          401 :         return LineSegment::setProperty(propertyId, v);
     955              :     }
     956            0 :     triggerLayout();
     957            0 :     return true;
     958              : }
     959              : 
     960          304 : PropertyValue GuitarBendSegment::propertyDefault(Pid id) const
     961              : {
     962          304 :     switch (id) {
     963            0 :     case Pid::BEND_VERTEX_OFF:
     964            0 :         return PointF();
     965          304 :     default:
     966          304 :         return LineSegment::propertyDefault(id);
     967              :     }
     968              : }
     969              : 
     970         1617 : EngravingObject* GuitarBendSegment::propertyDelegate(Pid id) const
     971              : {
     972         1617 :     switch (id) {
     973            0 :     case Pid::DIRECTION:
     974              :     case Pid::BEND_SHOW_HOLD_LINE:
     975              :     case Pid::POSITION_LINKED_TO_MASTER:
     976              :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     977              :     case Pid::GUITAR_DIVE_TAB_POS:
     978              :     case Pid::VIBRATO_LINE_TYPE:
     979            0 :         return guitarBend();
     980         1617 :     default:
     981         1617 :         return LineSegment::propertyDelegate(id);
     982              :     }
     983              : }
     984              : 
     985            0 : void GuitarBendSegment::reset()
     986              : {
     987            0 :     undoResetProperty(Pid::DIRECTION);
     988            0 :     undoResetProperty(Pid::BEND_VERTEX_OFF);
     989            0 :     LineSegment::reset();
     990            0 : }
     991              : 
     992          164 : double GuitarBendSegment::lineWidth() const
     993              : {
     994          164 :     return guitarBend()->lineWidth();
     995              : }
     996              : 
     997            8 : void GuitarBendSegment::scanElements(std::function<void(EngravingItem*)> func)
     998              : {
     999            8 :     func(m_text);
    1000            8 :     LineSegment::scanElements(func);
    1001            8 : }
    1002              : 
    1003          304 : bool GuitarBendSegment::isUserModified() const
    1004              : {
    1005          304 :     bool modified = !vertexPointOff().isNull() || (m_text && m_text->isUserModified());
    1006          304 :     return modified || LineSegment::isUserModified();
    1007              : }
    1008              : 
    1009            0 : Color GuitarBend::curColor(const rendering::PaintOptions& opt) const
    1010              : {
    1011            0 :     if (!opt.isPrinting && MScore::warnGuitarBends) {
    1012            0 :         if (m_isInvalid) {
    1013            0 :             return selected() ? configuration()->criticalSelectedColor() : configuration()->criticalColor();
    1014              :         }
    1015              : 
    1016            0 :         if (m_isBorderlineUnplayable) {
    1017            0 :             return selected() ? configuration()->warningSelectedColor() : configuration()->warningColor();
    1018              :         }
    1019              :     }
    1020              : 
    1021            0 :     return EngravingItem::curColor(opt);
    1022              : }
    1023              : 
    1024            0 : void GuitarBend::adaptBendsFromTabToStandardStaff(const Staff* staff)
    1025              : {
    1026              :     // On tabs, bends force end notes to be invisible. When switching to
    1027              :     // normal staff we need to turn all the end notes visible again.
    1028              : 
    1029            0 :     auto processBends = [](Chord* chord) {
    1030            0 :         for (Note* note : chord->notes()) {
    1031            0 :             GuitarBend* bend = note->bendFor();
    1032            0 :             if (!bend) {
    1033            0 :                 continue;
    1034              :             }
    1035            0 :             if (!bend->startNote()->overrideBendVisibilityRules()) {
    1036            0 :                 bend->startNote()->setVisible(true);
    1037              :             }
    1038            0 :             if (!bend->endNote()->overrideBendVisibilityRules()) {
    1039            0 :                 bend->endNote()->setVisible(true);
    1040              :             }
    1041              :         }
    1042            0 :     };
    1043              : 
    1044            0 :     staff_idx_t staffIdx = staff->idx();
    1045            0 :     track_idx_t startTrack = staff2track(staffIdx);
    1046            0 :     track_idx_t endTrack = startTrack + VOICES;
    1047            0 :     for (Segment* segment = staff->score()->firstSegment(SegmentType::ChordRest); segment; segment = segment->next1()) {
    1048            0 :         if (!segment->isChordRestType()) {
    1049            0 :             continue;
    1050              :         }
    1051            0 :         for (track_idx_t track = startTrack; track < endTrack; ++track) {
    1052            0 :             EngravingItem* item = segment->element(track);
    1053            0 :             if (!item || !item->isChord()) {
    1054            0 :                 continue;
    1055              :             }
    1056            0 :             Chord* chord = toChord(item);
    1057            0 :             processBends(chord);
    1058            0 :             for (Chord* grace : chord->graceNotes()) {
    1059            0 :                 processBends(grace);
    1060              :             }
    1061              :         }
    1062              :     }
    1063            0 : }
    1064              : 
    1065            0 : GuitarBendType GuitarBend::bendTypeFromActionIcon(ActionIconType actionIconType)
    1066              : {
    1067            0 :     switch (actionIconType) {
    1068            0 :     case ActionIconType::STANDARD_BEND: return GuitarBendType::BEND;
    1069            0 :     case ActionIconType::PRE_BEND: return GuitarBendType::PRE_BEND;
    1070            0 :     case ActionIconType::GRACE_NOTE_BEND: return GuitarBendType::GRACE_NOTE_BEND;
    1071            0 :     case ActionIconType::SLIGHT_BEND: return GuitarBendType::SLIGHT_BEND;
    1072            0 :     case ActionIconType::DIVE: return GuitarBendType::DIVE;
    1073            0 :     case ActionIconType::PRE_DIVE: return GuitarBendType::PRE_DIVE;
    1074            0 :     case ActionIconType::DIP: return GuitarBendType::DIP;
    1075            0 :     case ActionIconType::SCOOP: return GuitarBendType::SCOOP;
    1076            0 :     default:
    1077            0 :         return GuitarBendType::BEND;
    1078              :     }
    1079              : }
    1080              : 
    1081          395 : void GuitarBend::setBendType(GuitarBendType t)
    1082              : {
    1083          395 :     m_bendType = t;
    1084              : 
    1085          395 :     resetProperty(Pid::GUITAR_BEND_AMOUNT);
    1086          395 :     resetProperty(Pid::BEND_START_TIME_FACTOR);
    1087          395 :     resetProperty(Pid::BEND_END_TIME_FACTOR);
    1088          395 :     resetProperty(Pid::BEND_TARGET_TIME_FACTOR);
    1089          395 : }
    1090              : 
    1091            4 : void GuitarBend::setTargetTimeFactor(float f)
    1092              : {
    1093            4 :     IF_ASSERT_FAILED(muse::RealIsEqualOrLess(f, endTimeFactor())) {
    1094            0 :         return;
    1095              :     }
    1096              : 
    1097            4 :     m_targetTimeFactor = f;
    1098              : }
    1099              : 
    1100              : /****************************************
    1101              :  *            GuitarBendHold
    1102              :  * **************************************/
    1103              : 
    1104            3 : GuitarBendHold::GuitarBendHold(GuitarBend* parent)
    1105            3 :     : SLine(ElementType::GUITAR_BEND_HOLD, parent, ElementFlag::MOVABLE)
    1106              : {
    1107            3 :     resetProperty(Pid::LINE_STYLE);
    1108            3 : }
    1109              : 
    1110            0 : GuitarBendHold::GuitarBendHold(const GuitarBendHold& h)
    1111            0 :     : SLine(h)
    1112              : {
    1113            0 : }
    1114              : 
    1115            2 : LineSegment* GuitarBendHold::createLineSegment()
    1116              : {
    1117            2 :     GuitarBendHoldSegment* seg = new GuitarBendHoldSegment(this);
    1118            2 :     seg->setTrack(track());
    1119            2 :     seg->setColor(lineColor());
    1120            2 :     return seg;
    1121              : }
    1122              : 
    1123            3 : PropertyValue GuitarBendHold::propertyDefault(Pid id) const
    1124              : {
    1125            3 :     switch (id) {
    1126            3 :     case Pid::LINE_STYLE:
    1127            3 :         return LineType::DASHED;
    1128            0 :     default:
    1129            0 :         return SLine::propertyDefault(id);
    1130              :     }
    1131              : }
    1132              : 
    1133            0 : Note* GuitarBendHold::startNote() const
    1134              : {
    1135            0 :     EngravingItem* startEl = startElement();
    1136            0 :     assert(startEl && startEl->isNote());
    1137            0 :     return toNote(startEl);
    1138              : }
    1139              : 
    1140            6 : Note* GuitarBendHold::endNote() const
    1141              : {
    1142            6 :     EngravingItem* endEl = endElement();
    1143            6 :     assert(endEl && endEl->isNote());
    1144            6 :     return toNote(endEl);
    1145              : }
    1146              : 
    1147            6 : double GuitarBendHold::lineWidth() const
    1148              : {
    1149            6 :     return style().styleAbsolute(parent() && toGuitarBend(parent())->isDive() ? Sid::guitarDiveLineWidthTab : Sid::guitarBendLineWidthTab);
    1150              : }
    1151              : 
    1152              : /****************************************
    1153              :  *         GuitarBendHoldSegment
    1154              :  * **************************************/
    1155              : 
    1156            2 : GuitarBendHoldSegment::GuitarBendHoldSegment(GuitarBendHold* sp)
    1157            2 :     : LineSegment(ElementType::GUITAR_BEND_HOLD_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
    1158              : {
    1159            2 :     setFlag(ElementFlag::ON_STAFF, true);
    1160            2 : }
    1161              : 
    1162            0 : void GuitarBendHoldSegment::dragGrip(EditData& ed)
    1163              : {
    1164            0 :     PointF delta = ed.evtDelta;
    1165              : 
    1166            0 :     switch (ed.curGrip) {
    1167            0 :     case Grip::START:
    1168            0 :         setOffset(offset() + delta);
    1169            0 :         setUserOff2(userOff2() - delta);
    1170            0 :         break;
    1171            0 :     case Grip::END:
    1172            0 :         setUserOff2(userOff2() + delta);
    1173            0 :         break;
    1174            0 :     case Grip::MIDDLE:
    1175            0 :         setOffset(offset() + delta);
    1176            0 :         break;
    1177            0 :     default:
    1178            0 :         break;
    1179              :     }
    1180              : 
    1181            0 :     triggerLayout();
    1182            0 : }
    1183              : 
    1184              : /****************************************
    1185              :  *         GuitarBendText
    1186              :  * **************************************/
    1187              : 
    1188              : static const ElementStyle guitarBendStyle {};
    1189              : 
    1190          397 : GuitarBendText::GuitarBendText(GuitarBendSegment* parent)
    1191          397 :     : TextBase(ElementType::GUITAR_BEND_TEXT, parent, TextStyleType::BEND)
    1192              : {
    1193          397 :     initElementStyle(&guitarBendStyle);
    1194          397 : }
    1195              : } // namespace mu::engraving
        

Generated by: LCOV version 2.0-1