LCOV - code coverage report
Current view: top level - src/engraving/dom - tiejumppointlist.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 60.2 % 133 80
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) 2025 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 "tiejumppointlist.h"
      24              : 
      25              : #include "../editing/edittie.h"
      26              : 
      27              : #include "barline.h"
      28              : #include "chord.h"
      29              : #include "factory.h"
      30              : #include "marker.h"
      31              : #include "measure.h"
      32              : #include "note.h"
      33              : #include "partialtie.h"
      34              : #include "score.h"
      35              : #include "segment.h"
      36              : #include "tie.h"
      37              : #include "volta.h"
      38              : 
      39              : using namespace mu::engraving;
      40              : 
      41              : //---------------------------------------------------------
      42              : //   TieJumpPoint
      43              : //---------------------------------------------------------
      44              : 
      45          189 : TieJumpPoint::TieJumpPoint(Note* note, bool active, int idx, bool followingNote)
      46          189 :     : m_note(note), m_active(active), m_followingNote(followingNote)
      47              : {
      48          189 :     m_id = u"jumpPoint" + String::fromStdString(std::to_string(idx));
      49          189 :     if (active && endTie()) {
      50          132 :         endTie()->setJumpPoint(this);
      51              :     }
      52          189 : }
      53              : 
      54          639 : Tie* TieJumpPoint::endTie() const
      55              : {
      56          639 :     return m_note ? m_note->tieBack() : nullptr;
      57              : }
      58              : 
      59           50 : void TieJumpPoint::undoSetActive(bool v)
      60              : {
      61           50 :     Score* score = m_note ? m_note->score() : nullptr;
      62           50 :     if (!score || m_active == v) {
      63           17 :         return;
      64              :     }
      65           33 :     score->undo(new ChangeTieJumpPointActive(m_jumpPointList, m_id, v));
      66              : }
      67              : 
      68            0 : TranslatableString TieJumpPoint::menuTitle() const
      69              : {
      70            0 :     const Measure* measure = m_note->findMeasure();
      71            0 :     const int measureNo = measure ? measure->measureNumber() + 1 : 0;
      72              : 
      73              :     //: %1 represents the preceding jump item eg. coda. %2 represents the measure number
      74            0 :     return TranslatableString("engraving", "Tie to %1 (m. %2)").arg(precedingJumpItemName(), String::number(measureNo));
      75              : }
      76              : 
      77            0 : String TieJumpPoint::precedingJumpItemName() const
      78              : {
      79            0 :     const Chord* startChord = m_note->chord();
      80            0 :     const Segment* seg = startChord->segment();
      81            0 :     const Measure* measure = seg->measure();
      82              : 
      83            0 :     if (seg->score()->firstSegment(SegmentType::ChordRest) == seg) {
      84              :         //: Used at %1 in the string "Tie to %1 (m. %2)"
      85            0 :         return muse::mtrc("engraving", "start of score", "partial tie menu");
      86              :     }
      87              : 
      88              :     // Markers
      89            0 :     for (const EngravingItem* e : measure->el()) {
      90            0 :         if (!e->isMarker()) {
      91            0 :             continue;
      92              :         }
      93              : 
      94            0 :         const Marker* marker = toMarker(e);
      95            0 :         if (marker->isRightMarker()) {
      96            0 :             continue;
      97              :         }
      98              : 
      99            0 :         if (marker->markerType() == MarkerType::CODA || marker->markerType() == MarkerType::VARCODA) {
     100              :             //: Used at %1 in the string "Tie to %1 (m. %2)"
     101            0 :             return muse::mtrc("engraving", "coda", "partial tie menu");
     102              :         } else {
     103              :             //: Used at %1 in the string "Tie to %1 (m. %2)"
     104            0 :             return muse::mtrc("engraving", "segno", "partial tie menu");
     105              :         }
     106              :     }
     107              : 
     108              :     // Voltas
     109            0 :     auto spanners = m_note->score()->spannerMap().findOverlapping(measure->tick().ticks(), measure->tick().ticks());
     110            0 :     for (auto& spanner : spanners) {
     111            0 :         if (!spanner.value->isVolta() || Fraction::fromTicks(spanner.start) != startChord->tick()) {
     112            0 :             continue;
     113              :         }
     114              : 
     115            0 :         Volta* volta = toVolta(spanner.value);
     116              : 
     117              :         //: Used at %1 in the string "Tie to %1 (m. %2)". %1 in this string represents the volta's text set by the user
     118            0 :         return muse::mtrc("engraving", "“%1” volta", "partial tie menu").arg(volta->beginText());
     119              :     }
     120              : 
     121              :     // Repeat barlines
     122            0 :     if (measure->repeatStart()) {
     123              :         //: Used at %1 in the string "Tie to %1 (m. %2)"
     124            0 :         return muse::mtrc("engraving", "start repeat", "partial tie menu");
     125              :     }
     126              : 
     127            0 :     for (Segment* prevSeg = seg->prev(SegmentType::BarLineTypes); prevSeg && prevSeg->tick() == seg->tick();
     128            0 :          prevSeg = prevSeg->prev(SegmentType::BarLineTypes)) {
     129            0 :         EngravingItem* el = prevSeg->element(startChord->track());
     130            0 :         if (!el || !el->isBarLine()) {
     131            0 :             continue;
     132              :         }
     133              : 
     134            0 :         BarLine* bl = toBarLine(el);
     135            0 :         if (bl->barLineType() & (BarLineType::START_REPEAT | BarLineType::END_START_REPEAT)) {
     136              :             //: Used at %1 in the string "Tie to %1 (m. %2)"
     137            0 :             return muse::mtrc("engraving", "start repeat", "partial tie menu");
     138              :         }
     139              :     }
     140              : 
     141            0 :     if (m_followingNote) {
     142              :         //: Used at %1 in the string "Tie to %1 (m. %2)"
     143            0 :         return muse::mtrc("engraving", "next note", "partial tie menu");
     144              :     }
     145              : 
     146              :     //: Used at %1 in the string "Tie to %1 (m. %2)"
     147            0 :     return muse::mtrc("engraving", "invalid", "partial tie menu");
     148            0 : }
     149              : 
     150              : //---------------------------------------------------------
     151              : //   TieJumpPointList
     152              : //---------------------------------------------------------
     153              : 
     154        36903 : TieJumpPointList::~TieJumpPointList()
     155              : {
     156        36903 :     muse::DeleteAll(m_jumpPoints);
     157        36903 :     m_jumpPoints.clear();
     158        36903 : }
     159              : 
     160          189 : void TieJumpPointList::add(TieJumpPoint* item)
     161              : {
     162          189 :     item->setJumpPointList(this);
     163          189 :     m_jumpPoints.push_back(item);
     164          189 : }
     165              : 
     166        17322 : void TieJumpPointList::clear()
     167              : {
     168        17465 :     for (const TieJumpPoint* jumpPoint : m_jumpPoints) {
     169          143 :         Tie* endTie = jumpPoint->endTie();
     170          143 :         if (!endTie) {
     171           28 :             continue;
     172              :         }
     173          115 :         endTie->setJumpPoint(nullptr);
     174              :     }
     175        17322 :     muse::DeleteAll(m_jumpPoints);
     176        17322 :     m_jumpPoints.clear();
     177        17322 : }
     178              : 
     179         7830 : Tie* TieJumpPointList::startTie() const
     180              : {
     181         7830 :     return m_note ? m_note->tieFor() : nullptr;
     182              : }
     183              : 
     184           54 : TieJumpPoint* TieJumpPointList::findJumpPoint(const String& id)
     185              : {
     186           95 :     for (TieJumpPoint* jumpPoint : m_jumpPoints) {
     187           95 :         if (jumpPoint->id() != id) {
     188           41 :             continue;
     189              :         }
     190              : 
     191           54 :         return jumpPoint;
     192              :     }
     193            0 :     return nullptr;
     194              : }
     195              : 
     196            8 : void TieJumpPointList::toggleJumpPoint(const String& id)
     197              : {
     198            8 :     TieJumpPoint* end = findJumpPoint(id);
     199              : 
     200            8 :     if (!end) {
     201            0 :         LOGE() << "No partial tie end point found with id: " << id;
     202            0 :         return;
     203              :     }
     204              : 
     205            8 :     Score* score = end->note() ? end->note()->score() : nullptr;
     206            8 :     if (!score) {
     207            0 :         return;
     208              :     }
     209              : 
     210            8 :     score->startCmd(TranslatableString("engraving", "Toggle partial tie"));
     211            8 :     const bool checked = end->active();
     212            8 :     if (checked) {
     213            8 :         undoRemoveTieFromScore(end);
     214              :     } else {
     215            0 :         undoAddTieToScore(end);
     216              :     }
     217            8 :     score->endCmd();
     218              : }
     219              : 
     220           16 : void TieJumpPointList::undoAddTieToScore(TieJumpPoint* jumpPoint)
     221              : {
     222           16 :     Note* note = jumpPoint->note();
     223           16 :     Score* score = note ? note->score() : nullptr;
     224           16 :     Tie* tie = startTie();
     225           16 :     if (!tie || !score) {
     226            0 :         return;
     227              :     }
     228              : 
     229           16 :     if (jumpPoint->followingNote()) {
     230              :         // Remove partial tie and add full tie
     231            0 :         if (!tie->isPartialTie() || !toPartialTie(tie)->isOutgoing()) {
     232            0 :             return;
     233              :         }
     234            0 :         jumpPoint->undoSetActive(true);
     235            0 :         Tie::changeTieType(tie, note);
     236            0 :         return;
     237              :     }
     238              : 
     239           16 :     jumpPoint->undoSetActive(true);
     240              : 
     241              :     // Check if there is already a tie.  If so, add partial tie info to it
     242           16 :     Tie* tieBack = note->tieBack();
     243           16 :     if (tieBack && !tieBack->isPartialTie()) {
     244            1 :         tieBack->setJumpPoint(jumpPoint);
     245            1 :         return;
     246              :     }
     247              :     // Otherwise create incoming partial tie on note
     248           15 :     PartialTie* pt = Factory::createPartialTie(note);
     249           15 :     pt->setEndNote(note);
     250           15 :     pt->setJumpPoint(jumpPoint);
     251           15 :     score->undoAddElement(pt);
     252              : }
     253              : 
     254           16 : void TieJumpPointList::undoRemoveTieFromScore(TieJumpPoint* jumpPoint)
     255              : {
     256           16 :     Note* note = jumpPoint->note();
     257           16 :     Score* score = note ? note->score() : nullptr;
     258           16 :     Tie* tie = startTie();
     259           16 :     if (!tie || !score) {
     260            0 :         return;
     261              :     }
     262              : 
     263           16 :     if (jumpPoint->followingNote()) {
     264              :         // Remove full tie and add partial tie
     265            4 :         if (tie->isPartialTie()) {
     266            0 :             return;
     267              :         }
     268            4 :         jumpPoint->undoSetActive(false);
     269              : 
     270            4 :         Tie::changeTieType(tie);
     271            4 :         return;
     272              :     }
     273              : 
     274           12 :     jumpPoint->undoSetActive(false);
     275              : 
     276              :     // Check if there is a full tie. If so, remove partial tie info from it
     277           12 :     Tie* tieBack = note->tieBack();
     278           12 :     if (tieBack && !tieBack->isPartialTie()) {
     279            2 :         tieBack->setJumpPoint(nullptr);
     280            2 :         return;
     281              :     }
     282              :     // Otherwise remove incoming partial tie on note
     283           10 :     PartialTie* pt = note->incomingPartialTie();
     284           10 :     if (!pt) {
     285            0 :         return;
     286              :     }
     287           10 :     score->undoRemoveElement(pt);
     288              : }
        

Generated by: LCOV version 2.0-1