LCOV - code coverage report
Current view: top level - src/engraving/dom - lyrics.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 52.0 % 298 155
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 "lyrics.h"
      24              : 
      25              : #include "types/translatablestring.h"
      26              : 
      27              : #include "../editing/navigation.h"
      28              : #include "../editing/textedit.h"
      29              : 
      30              : #include "measure.h"
      31              : #include "score.h"
      32              : #include "segment.h"
      33              : #include "staff.h"
      34              : #include "text.h"
      35              : #include "utils.h"
      36              : 
      37              : #include "log.h"
      38              : 
      39              : using namespace mu;
      40              : using namespace mu::engraving;
      41              : 
      42              : namespace mu::engraving {
      43              : //---------------------------------------------------------
      44              : //   lyricsElementStyle
      45              : //---------------------------------------------------------
      46              : 
      47              : static const ElementStyle lyricsElementStyle {
      48              :     { Sid::lyricsPlacement, Pid::PLACEMENT },
      49              :     { Sid::lyricsAvoidBarlines, Pid::AVOID_BARLINES },
      50              : };
      51              : 
      52              : //---------------------------------------------------------
      53              : //   Lyrics
      54              : //---------------------------------------------------------
      55              : 
      56         1781 : Lyrics::Lyrics(DummyParentOr<ChordRest> parent)
      57         1781 :     : TextBase(ElementType::LYRICS, parent, TextStyleType::LYRICS_ODD, ElementFlag::ON_STAFF)
      58              : {
      59         1781 :     m_separator  = 0;
      60         1781 :     initElementStyle(&lyricsElementStyle);
      61         1781 :     m_verse         = 0;
      62         1781 :     m_ticks      = Fraction(0, 1);
      63         1781 :     m_syllabic   = LyricsSyllabic::SINGLE;
      64         1781 : }
      65              : 
      66         1331 : Lyrics::Lyrics(const Lyrics& l)
      67         1331 :     : TextBase(l)
      68              : {
      69         1331 :     m_verse        = l.m_verse;
      70         1331 :     m_ticks     = l.m_ticks;
      71         1331 :     m_syllabic  = l.m_syllabic;
      72         1331 :     m_separator = 0;
      73         1331 : }
      74              : 
      75         6180 : Lyrics::~Lyrics()
      76              : {
      77         3090 :     if (m_separator) {
      78          225 :         remove(m_separator);
      79              :     }
      80         6180 : }
      81              : 
      82            0 : TranslatableString Lyrics::subtypeUserName() const
      83              : {
      84            0 :     return TranslatableString("engraving", "Verse %1").arg(m_verse + 1);
      85              : }
      86              : 
      87              : //---------------------------------------------------------
      88              : //   add
      89              : //---------------------------------------------------------
      90              : 
      91            0 : void Lyrics::add(EngravingItem* el)
      92              : {
      93            0 :     if (el->isLyricsLine()) {
      94            0 :         LyricsLine* separator = toLyricsLine(el);
      95            0 :         m_separator = separator;
      96            0 :         score()->addUnmanagedSpanner(separator);
      97              :     } else {
      98            0 :         LOGD("Lyrics::add: unknown element %s", el->typeName());
      99              :     }
     100            0 : }
     101              : 
     102              : //---------------------------------------------------------
     103              : //   remove
     104              : //---------------------------------------------------------
     105              : 
     106          464 : void Lyrics::remove(EngravingItem* el)
     107              : {
     108          464 :     if (el->isLyricsLine()) {
     109              :         // only if separator still exists and is the right one
     110          464 :         if (m_separator && el == m_separator) {
     111              :             // Lyrics::remove() and LyricsLine::removeUnmanaged() call each other;
     112              :             // be sure each finds a clean context
     113          232 :             LyricsLine* separ = m_separator;
     114          232 :             m_separator = 0;
     115          232 :             separ->removeUnmanaged();
     116              :         }
     117              :     } else {
     118            0 :         LOGD("Lyrics::remove: unknown element %s", el->typeName());
     119              :     }
     120          464 : }
     121              : 
     122              : //---------------------------------------------------------
     123              : //   isMelisma
     124              : //---------------------------------------------------------
     125              : 
     126         4651 : bool Lyrics::isMelisma() const
     127              : {
     128              :     // entered as melisma using underscore?
     129         4651 :     if (m_ticks > Fraction(0, 1)) {
     130          102 :         return true;
     131              :     }
     132              : 
     133              :     // hyphenated?
     134              :     // if so, it is a melisma only if there is no lyric in same verse on next CR
     135         4549 :     if (m_separator && (m_syllabic == LyricsSyllabic::BEGIN || m_syllabic == LyricsSyllabic::MIDDLE)) {
     136              :         // find next CR and check for existence of lyric in same verse and placement (in any voice)
     137          367 :         const ChordRest* cr = chordRest();
     138          367 :         if (cr) {
     139          367 :             const Segment* s = cr->segment()->next1();
     140          367 :             const track_idx_t strack = staffIdx() * VOICES;
     141          367 :             const track_idx_t etrack = strack + VOICES;
     142          367 :             const track_idx_t lyrTrack = track();
     143          367 :             const ChordRest* lyrVoiceNextCR = s ? s->nextChordRest(lyrTrack) : nullptr;
     144          601 :             for (track_idx_t track = strack; track < etrack; ++track) {
     145          544 :                 const ChordRest* trackNextCR = s ? s->nextChordRest(track) : nullptr;
     146          544 :                 if (trackNextCR) {
     147            7 :                     if (lyrTrack != track && lyrVoiceNextCR
     148          380 :                         && !lyrVoiceNextCR->lyrics(m_verse, placement()) && lyrVoiceNextCR->tick() < trackNextCR->tick()) {
     149              :                         // There is an intermediary note in a different voice, this is a melisma
     150            1 :                         return true;
     151              :                     }
     152          372 :                     if (trackNextCR->lyrics(m_verse, placement())) {
     153              :                         // Next note has lyrics, not a melisma just a dash
     154          309 :                         return false;
     155              :                     }
     156              :                 }
     157              :             }
     158           57 :             return true;
     159              :         }
     160              :     }
     161              : 
     162              :     // default - not a melisma
     163         4182 :     return false;
     164              : }
     165              : 
     166              : //---------------------------------------------------------
     167              : //   paste
     168              : //---------------------------------------------------------
     169              : 
     170            0 : void Lyrics::paste(const String& txt)
     171              : {
     172            0 :     if (txt.startsWith('<') && txt.contains('>')) {
     173            0 :         TextBase::paste(txt);
     174            0 :         return;
     175              :     }
     176              : 
     177            0 :     String regex = String(u"[^\\S") + Char(0xa0) + Char(0x202F) + u"]+";
     178            0 :     StringList sl = txt.split(std::regex(regex.toStdString()), muse::SkipEmptyParts);
     179            0 :     if (sl.empty()) {
     180            0 :         return;
     181              :     }
     182              : 
     183            0 :     StringList hyph = sl.at(0).split(u'-');
     184            0 :     score()->startCmd(TranslatableString("undoableAction", "Paste lyrics"));
     185              : 
     186            0 :     deleteSelectedText();
     187              : 
     188            0 :     if (hyph.size() > 1) {
     189            0 :         score()->undo(new InsertText(cursor(), hyph[0]));
     190            0 :         hyph.removeAt(0);
     191            0 :         sl[0] =  hyph.join(u"-");
     192            0 :     } else if (sl.size() > 1 && sl[1] == u"-") {
     193            0 :         score()->undo(new InsertText(cursor(), sl[0]));
     194            0 :         sl.removeAt(0);
     195            0 :         sl.removeAt(0);
     196            0 :     } else if (sl[0].startsWith(u"_")) {
     197            0 :         sl[0].remove(0, 1);
     198            0 :         if (sl[0].isEmpty()) {
     199            0 :             sl.removeAt(0);
     200              :         }
     201            0 :     } else if (sl[0].contains(u"_")) {
     202            0 :         size_t p = sl[0].indexOf(u'_');
     203            0 :         score()->undo(new InsertText(cursor(), sl[0]));
     204            0 :         sl[0] = sl[0].mid(p + 1);
     205            0 :         if (sl[0].isEmpty()) {
     206            0 :             sl.removeAt(0);
     207              :         }
     208            0 :     } else if (sl.size() > 1 && sl[1] == "_") {
     209            0 :         score()->undo(new InsertText(cursor(), sl[0]));
     210            0 :         sl.removeAt(0);
     211            0 :         sl.removeAt(0);
     212              :     } else {
     213            0 :         score()->undo(new InsertText(cursor(), sl[0]));
     214            0 :         sl.removeAt(0);
     215              :     }
     216              : 
     217            0 :     score()->endCmd();
     218            0 : }
     219              : 
     220              : //---------------------------------------------------------
     221              : //   endTick
     222              : //---------------------------------------------------------
     223              : 
     224          203 : Fraction Lyrics::endTick() const
     225              : {
     226          203 :     return segment()->tick() + ticks();
     227              : }
     228              : 
     229              : //---------------------------------------------------------
     230              : //   acceptDrop
     231              : //---------------------------------------------------------
     232              : 
     233            0 : bool Lyrics::acceptDrop(EditData& data) const
     234              : {
     235            0 :     return data.dropElement->isText() || TextBase::acceptDrop(data);
     236              : }
     237              : 
     238              : //---------------------------------------------------------
     239              : //   drop
     240              : //---------------------------------------------------------
     241              : 
     242            0 : EngravingItem* Lyrics::drop(Transaction& tx, EditData& data)
     243              : {
     244            0 :     ElementType type = data.dropElement->type();
     245            0 :     if (type == ElementType::SYMBOL || type == ElementType::FSYMBOL) {
     246            0 :         TextBase::drop(tx, data);
     247            0 :         return 0;
     248              :     }
     249            0 :     if (!data.dropElement->isText()) {
     250            0 :         delete data.dropElement;
     251            0 :         data.dropElement = 0;
     252            0 :         return 0;
     253              :     }
     254            0 :     Text* e = toText(data.dropElement);
     255            0 :     e->setOwnershipParent(this);
     256            0 :     score()->undoAddElement(e);
     257            0 :     return e;
     258              : }
     259              : 
     260            0 : bool Lyrics::isEditAllowed(EditData& ed) const
     261              : {
     262            0 :     if (isTextNavigationKey(ed.key, ed.modifiers)) {
     263            0 :         return false;
     264              :     }
     265              : 
     266              :     static const std::set<KeyboardModifiers> navigationModifiers {
     267              :         NoModifier,
     268              :         KeypadModifier,
     269              :         ShiftModifier
     270            0 :     };
     271              : 
     272            0 :     if (navigationModifiers.find(ed.modifiers) != navigationModifiers.end()) {
     273              :         static const std::set<int> navigationKeys {
     274              :             Key_Underscore,
     275              :             Key_Minus,
     276              :             Key_Enter,
     277              :             Key_Return,
     278              :             Key_Up,
     279              :             Key_Down
     280            0 :         };
     281              : 
     282            0 :         if (navigationKeys.find(ed.key) != navigationKeys.end()) {
     283            0 :             return false;
     284              :         }
     285              :     }
     286              : 
     287            0 :     if (ed.key == Key_Left || ed.key == Key_Backspace) {
     288            0 :         return cursor()->column() != 0 || cursor()->hasSelection();
     289              :     }
     290              : 
     291            0 :     if (ed.key == Key_Right) {
     292            0 :         bool cursorInLastColumn = cursor()->column() == cursor()->curLine().columns();
     293            0 :         return !cursorInLastColumn || cursor()->hasSelection();
     294              :     }
     295              : 
     296            0 :     return TextBase::isEditAllowed(ed);
     297              : }
     298              : 
     299            0 : void Lyrics::adjustPrevious()
     300              : {
     301            0 :     Lyrics* prev = Navigation::prevLyrics(toLyrics(this));
     302            0 :     if (prev && prev->ticks().isNotZero()) {
     303              :         // search for lyric spanners to split at this point if necessary
     304            0 :         if (prev->tick() + prev->ticks() >= tick()) {
     305              :             // the previous lyric has a spanner attached that goes through this one
     306              :             // we need to shorten it
     307            0 :             Segment* s = score()->tick2segment(tick());
     308            0 :             if (s) {
     309            0 :                 s = s->prev1(SegmentType::ChordRest);
     310            0 :                 if (s->tick() > prev->tick()) {
     311            0 :                     prev->undoChangeProperty(Pid::LYRIC_TICKS, s->tick() - prev->tick());
     312              :                 } else {
     313            0 :                     prev->undoChangeProperty(Pid::LYRIC_TICKS, Fraction::eps());
     314              :                 }
     315              :             }
     316              :         }
     317            0 :         prev->setNeedRemoveInvalidSegments();
     318            0 :         prev->triggerLayout();
     319              :     }
     320            0 : }
     321              : 
     322            2 : void Lyrics::setNeedRemoveInvalidSegments()
     323              : {
     324              :     // Allow "invalid" segments when there is a following repeat item
     325              : 
     326            2 :     const Measure* meas = measure();
     327            2 :     const ChordRest* separatorEndChord = m_separator ? toChordRest(m_separator->endElement()) : nullptr;
     328            2 :     const ChordRest* lastChordRest = meas ? meas->lastChordRest(track()) : nullptr;
     329            2 :     const bool endChordIsLastInMeasure = separatorEndChord == lastChordRest;
     330            2 :     const bool hasFollowingJump = lastChordRest ? lastChordRest->hasFollowingJumpItem() : false;
     331              : 
     332            2 :     if (endChordIsLastInMeasure && hasFollowingJump) {
     333            0 :         return;
     334              :     }
     335            2 :     m_needRemoveInvalidSegments = true;
     336              : }
     337              : 
     338              : //---------------------------------------------------------
     339              : //   endEdit
     340              : //---------------------------------------------------------
     341              : 
     342            0 : void Lyrics::endEdit(EditData& ed)
     343              : {
     344            0 :     TextBase::endEdit(ed);
     345              : 
     346            0 :     triggerLayout();
     347            0 :     if (m_separator) {
     348            0 :         m_separator->triggerLayout();
     349              :     }
     350            0 : }
     351              : 
     352              : //---------------------------------------------------------
     353              : //   removeFromScore
     354              : //---------------------------------------------------------
     355              : 
     356           44 : void Lyrics::removeFromScore()
     357              : {
     358           44 :     if (m_ticks.isNotZero()) {
     359              :         // clear melismaEnd flag from end cr
     360            0 :         ChordRest* ecr = score()->findCR(endTick(), track());
     361            0 :         if (ecr) {
     362            0 :             ecr->setMelismaEnd(false);
     363              :         }
     364              :     }
     365              : 
     366           44 :     if (!plainText().isEmpty()) {
     367           44 :         PartialLyricsLine* partialDash = findPrevPartialLyricsLineDash(this);
     368           44 :         if (partialDash) {
     369            0 :             score()->undoRemoveElement(partialDash);
     370              :         }
     371              :     }
     372              : 
     373           44 :     if (m_separator) {
     374            0 :         m_separator->removeUnmanaged();
     375            0 :         delete m_separator;
     376            0 :         m_separator = 0;
     377              :     }
     378           44 :     Lyrics* prev = Navigation::prevLyrics(this);
     379           44 :     if (prev) {
     380              :         // check to make sure we haven't created an invalid segment by deleting this lyric
     381            0 :         prev->setNeedRemoveInvalidSegments();
     382              :     }
     383           44 : }
     384              : 
     385              : //---------------------------------------------------------
     386              : //   getProperty
     387              : //---------------------------------------------------------
     388              : 
     389        13338 : PropertyValue Lyrics::getProperty(Pid propertyId) const
     390              : {
     391        13338 :     switch (propertyId) {
     392            0 :     case Pid::SYLLABIC:
     393            0 :         return int(m_syllabic);
     394          457 :     case Pid::LYRIC_TICKS:
     395          457 :         return m_ticks;
     396          455 :     case Pid::VERSE:
     397          455 :         return m_verse;
     398            0 :     case Pid::AVOID_BARLINES:
     399            0 :         return m_avoidBarlines;
     400        12426 :     default:
     401        12426 :         return TextBase::getProperty(propertyId);
     402              :     }
     403              : }
     404              : 
     405              : //---------------------------------------------------------
     406              : //   setProperty
     407              : //---------------------------------------------------------
     408              : 
     409        40436 : bool Lyrics::setProperty(Pid propertyId, const PropertyValue& v)
     410              : {
     411        40436 :     ChordRest* scr = nullptr;
     412        40436 :     ChordRest* ecr = nullptr;
     413              : 
     414        40436 :     switch (propertyId) {
     415         1800 :     case Pid::PLACEMENT:
     416              :     {
     417         1800 :         PlacementV newVal = v.value<PlacementV>();
     418         1800 :         if (newVal != placement()) {
     419            0 :             if (Lyrics* l = Navigation::prevLyrics(this)) {
     420            0 :                 l->setNeedRemoveInvalidSegments();
     421              :             }
     422            0 :             if (Navigation::nextLyrics(this)) {
     423            0 :                 setNeedRemoveInvalidSegments();
     424              :             }
     425            0 :             setPlacement(newVal);
     426              :         }
     427              :     }
     428         1800 :     break;
     429            0 :     case Pid::SYLLABIC:
     430            0 :         m_syllabic = LyricsSyllabic(v.toInt());
     431            0 :         break;
     432            6 :     case Pid::LYRIC_TICKS:
     433            6 :         if (m_ticks.isNotZero()) {
     434              :             // clear melismaEnd flag from previous end cr
     435              :             // this might be premature, as there may be other melismas ending there
     436              :             // but flag will be generated correctly on layout
     437              :             // TODO: after inserting a measure,
     438              :             // endTick info is wrong.
     439              :             // Somehow we need to fix this.
     440              :             // See https://musescore.org/en/node/285304 and https://musescore.org/en/node/311289
     441            6 :             ecr = score()->findCR(endTick(), track());
     442            6 :             if (ecr) {
     443            6 :                 ecr->setMelismaEnd(false);
     444              :             }
     445              :         }
     446            6 :         scr = score()->findCR(tick(), track());
     447            6 :         m_ticks = v.value<Fraction>();
     448            6 :         if (scr && m_ticks <= scr->ticks()) {
     449              :             // if no ticks, we have to relayout in order to remove invalid melisma segments
     450            2 :             setNeedRemoveInvalidSegments();
     451              :         }
     452            6 :         break;
     453            4 :     case Pid::VERSE: {
     454            4 :         if (Lyrics* l = Navigation::prevLyrics(this)) {
     455            0 :             l->setNeedRemoveInvalidSegments();
     456              :         }
     457            4 :         bool followTextStyle = getProperty(Pid::TEXT_STYLE) == propertyDefault(Pid::TEXT_STYLE);
     458            4 :         m_verse = v.toInt();
     459            4 :         if (followTextStyle) {
     460            4 :             setProperty(Pid::TEXT_STYLE, propertyDefault(Pid::TEXT_STYLE));
     461              :         }
     462            4 :         break;
     463              :     }
     464         1798 :     case Pid::AVOID_BARLINES:
     465         1798 :         m_avoidBarlines = v.toBool();
     466         1798 :         break;
     467            2 :     case Pid::VISIBLE:
     468            2 :         setVisible(v.toBool());
     469            2 :         break;
     470        36826 :     default:
     471        36826 :         if (!TextBase::setProperty(propertyId, v)) {
     472            0 :             return false;
     473              :         }
     474        36826 :         break;
     475              :     }
     476        40436 :     triggerLayout();
     477        40436 :     return true;
     478              : }
     479              : 
     480              : //---------------------------------------------------------
     481              : //   propertyDefault
     482              : //---------------------------------------------------------
     483              : 
     484        23539 : PropertyValue Lyrics::propertyDefault(Pid id) const
     485              : {
     486        23539 :     switch (id) {
     487          451 :     case Pid::TEXT_STYLE:
     488          451 :         return isEven() ? TextStyleType::LYRICS_EVEN : TextStyleType::LYRICS_ODD;
     489          131 :     case Pid::PLACEMENT:
     490          131 :         return style().styleV(Sid::lyricsPlacement);
     491            0 :     case Pid::SYLLABIC:
     492            0 :         return int(LyricsSyllabic::SINGLE);
     493          443 :     case Pid::LYRIC_TICKS:
     494          886 :         return Fraction(0, 1);
     495          443 :     case Pid::VERSE:
     496          443 :         return 0;
     497            0 :     case Pid::AVOID_BARLINES:
     498            0 :         return style().styleB(Sid::lyricsAvoidBarlines);
     499         2469 :     case Pid::POSITION:
     500         2469 :         if (isMelisma()) {
     501            0 :             return style().styleV(Sid::lyricsMelismaAlign).value<Align>().horizontal;
     502              :         }
     503              :     // fall through
     504              :     default:
     505        22071 :         return TextBase::propertyDefault(id);
     506              :     }
     507              : }
     508              : 
     509        82193 : void Lyrics::triggerLayout() const
     510              : {
     511        82193 :     if (m_separator) {
     512              :         // The separator may extend to next system(s), so we must use Spanner::triggerLayout()
     513          145 :         m_separator->triggerLayout();
     514              :     } else {
     515              :         // In this case is ok to use EngravingItem::triggerLayout()
     516        82048 :         EngravingItem::triggerLayout();
     517              :     }
     518        82193 : }
     519              : 
     520         4330 : double Lyrics::yRelativeToStaff() const
     521              : {
     522         4330 :     const double yOff = staffOffsetY();
     523         4330 :     return pos().y() + chordRest()->pos().y() + yOff;
     524              : }
     525              : 
     526         2180 : void Lyrics::setYRelativeToStaff(double y)
     527              : {
     528         2180 :     const double yOff = staffOffsetY();
     529         2180 :     mutldata()->setPosY(y - chordRest()->pos().y() - yOff);
     530         2180 : }
     531              : 
     532              : //---------------------------------------------------------
     533              : //   forAllLyrics
     534              : //---------------------------------------------------------
     535              : 
     536            0 : void Score::forAllLyrics(std::function<void(Lyrics*)> f)
     537              : {
     538            0 :     for (Segment* s = firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
     539            0 :         for (EngravingItem* e : s->elist()) {
     540            0 :             if (e) {
     541            0 :                 for (Lyrics* l : toChordRest(e)->lyrics()) {
     542            0 :                     f(l);
     543              :                 }
     544              :             }
     545              :         }
     546              :     }
     547            0 : }
     548              : 
     549              : //---------------------------------------------------------
     550              : //   undoChangeProperty
     551              : //---------------------------------------------------------
     552              : 
     553            9 : void Lyrics::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
     554              : {
     555            9 :     if (id == Pid::VERSE && verse() != v.toInt()) {
     556            2 :         PartialLyricsLine* prevPartial = findPrevPartialLyricsLineDash(this);
     557              : 
     558            4 :         for (Lyrics* l : chordRest()->lyrics()) {
     559            4 :             if (l->verse() == v.toInt()) {
     560              :                 // verse already exists, swap
     561            2 :                 l->TextBase::undoChangeProperty(id, verse(), ps);
     562            2 :                 const PlacementV p = l->placement();
     563            2 :                 l->TextBase::undoChangeProperty(Pid::PLACEMENT, placement(), ps);
     564            2 :                 TextBase::undoChangeProperty(Pid::PLACEMENT, p, ps);
     565            2 :                 break;
     566              :             }
     567              :         }
     568            2 :         TextBase::undoChangeProperty(id, v, ps);
     569            2 :         if (prevPartial && prevPartial->verse() != v.toInt()) {
     570              :             // Skip logic to update Lyrics by calling parent class
     571            1 :             prevPartial->LyricsLine::undoChangeProperty(id, v, ps);
     572              :         }
     573            2 :         return;
     574            7 :     } else if (id == Pid::VISIBLE && separator()) {
     575            0 :         separator()->undoChangeProperty(Pid::VISIBLE, v.toBool(), ps);
     576              :     }
     577              : 
     578            7 :     TextBase::undoChangeProperty(id, v, ps);
     579              : }
     580              : 
     581              : //---------------------------------------------------------
     582              : //   reset
     583              : //---------------------------------------------------------
     584              : 
     585            0 : void Lyrics::reset()
     586              : {
     587            0 :     undoResetProperty(Pid::CENTER_BETWEEN_STAVES);
     588            0 :     TextBase::reset();
     589            0 : }
     590              : 
     591              : //---------------------------------------------------------
     592              : //   removeInvalidSegments
     593              : //
     594              : // Remove lyric-final melisma lines and reset the alignment of the lyric
     595              : //---------------------------------------------------------
     596              : 
     597            2 : void Lyrics::removeInvalidSegments()
     598              : {
     599            2 :     m_needRemoveInvalidSegments = false;
     600            2 :     if (m_separator && isMelisma() && m_ticks < m_separator->startCR()->ticks()) {
     601            0 :         setTicks(Fraction(0, 1));
     602            0 :         m_separator->setTicks(Fraction(0, 1));
     603            0 :         m_separator->removeUnmanaged();
     604            0 :         m_separator = nullptr;
     605            0 :         setPosition(propertyDefault(Pid::POSITION).value<AlignH>());
     606            0 :         if (m_syllabic == LyricsSyllabic::BEGIN || m_syllabic == LyricsSyllabic::SINGLE) {
     607            0 :             undoChangeProperty(Pid::SYLLABIC, int(LyricsSyllabic::SINGLE));
     608              :         } else {
     609            0 :             undoChangeProperty(Pid::SYLLABIC, int(LyricsSyllabic::END));
     610              :         }
     611              :     }
     612            2 : }
     613              : }
        

Generated by: LCOV version 2.0-1