LCOV - code coverage report
Current view: top level - src/engraving/dom - anchors.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 13.3 % 338 45
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 <climits>
      24              : 
      25              : #include "anchors.h"
      26              : #include "factory.h"
      27              : #include "figuredbass.h"
      28              : #include "fret.h"
      29              : #include "harmony.h"
      30              : #include "measure.h"
      31              : #include "page.h"
      32              : #include "score.h"
      33              : #include "spanner.h"
      34              : #include "staff.h"
      35              : #include "system.h"
      36              : #include "textlinebase.h"
      37              : #include "timesig.h"
      38              : #include "utils.h"
      39              : 
      40              : #include "rendering/score/measurelayout.h"
      41              : 
      42              : using namespace mu::engraving::rendering::score;
      43              : 
      44              : namespace mu::engraving {
      45              : /***************************************
      46              :  * EditTimeTickAnchors
      47              :  * ************************************/
      48              : 
      49            0 : void EditTimeTickAnchors::updateAnchors(const EngravingItem* item)
      50              : {
      51            0 :     if (!item->allowTimeAnchor()) {
      52            0 :         item->score()->hideAnchors();
      53            0 :         return;
      54              :     }
      55              : 
      56            0 :     Fraction startTickMainRegion = item->isSpannerSegment() ? toSpannerSegment(item)->spanner()->tick() : item->tick();
      57            0 :     Fraction endTickMainRegion = item->isSpannerSegment() ? toSpannerSegment(item)->spanner()->tick2() : item->tick();
      58              : 
      59            0 :     Score* score = item->score();
      60            0 :     Measure* startMeasure = score->tick2measure(startTickMainRegion);
      61            0 :     Measure* endMeasure = score->tick2measure(endTickMainRegion);
      62            0 :     if (!startMeasure || !endMeasure) {
      63            0 :         return;
      64              :     }
      65              : 
      66            0 :     staff_idx_t staff = item->staffIdx();
      67            0 :     Measure* startOneBefore = startMeasure->prevMeasure();
      68            0 :     for (MeasureBase* mb = startOneBefore ? startOneBefore : startMeasure; mb && mb->tick() <= endMeasure->tick(); mb = mb->next()) {
      69            0 :         if (!mb->isMeasure()) {
      70            0 :             continue;
      71              :         }
      72            0 :         updateAnchors(toMeasure(mb), staff);
      73              :     }
      74              : 
      75            0 :     Fraction startTickExtendedRegion = startMeasure->tick();
      76            0 :     Fraction endTickExtendedRegion = endMeasure->endTick();
      77            0 :     voice_idx_t voiceIdx =  item->hasVoiceAssignmentProperties() && item->getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>()
      78            0 :                            != VoiceAssignment::CURRENT_VOICE_ONLY ? VOICES : item->voice();
      79              : 
      80            0 :     score->setShowAnchors(ShowAnchors(voiceIdx, staff, startTickMainRegion, endTickMainRegion, startTickExtendedRegion,
      81            0 :                                       endTickExtendedRegion));
      82              : 
      83            0 :     item->triggerLayout();
      84              : }
      85              : 
      86            0 : void EditTimeTickAnchors::updateAnchors(Measure* measure, staff_idx_t staffIdx, const std::set<Fraction>& additionalAnchorRelTicks)
      87              : {
      88            0 :     Fraction startTick = Fraction(0, 1);
      89            0 :     Fraction endTick = measure->ticks();
      90              : 
      91            0 :     TimeSig* timeSig = measure->score()->staff(staffIdx)->timeSig(measure->tick());
      92            0 :     Fraction timeSigFrac = timeSig ? timeSig->sig() : measure->timesig();
      93            0 :     Fraction halfDivision = Fraction(1, 2 * timeSigFrac.denominator());
      94            0 :     if (timeSig) {
      95            0 :         halfDivision /= timeSig->stretch();
      96              :     }
      97              : 
      98            0 :     std::set<Fraction> anchorTicks { additionalAnchorRelTicks };
      99            0 :     for (Fraction tick = startTick; tick <= endTick; tick += halfDivision) {
     100            0 :         anchorTicks.insert(tick);
     101              :     }
     102            0 :     for (Segment* seg = measure->first(SegmentType::Duration); seg;
     103            0 :          seg = seg->next(SegmentType::Duration)) {
     104            0 :         anchorTicks.insert(seg->rtick());
     105              :     }
     106              : 
     107            0 :     for (const Fraction& anchorTick : anchorTicks) {
     108            0 :         createTimeTickAnchor(measure, anchorTick, staffIdx);
     109              :     }
     110              : 
     111            0 :     updateLayout(measure);
     112            0 : }
     113              : 
     114          204 : TimeTickAnchor* EditTimeTickAnchors::createTimeTickAnchor(Measure* measure, Fraction relTick, staff_idx_t staffIdx)
     115              : {
     116          204 :     TimeTickAnchor* returnAnchor = nullptr;
     117              : 
     118          204 :     Staff* staff = measure->score()->staff(staffIdx);
     119          204 :     IF_ASSERT_FAILED(staff) {
     120            0 :         return nullptr;
     121              :     }
     122              : 
     123          419 :     for (EngravingObject* linkedObj : staff->linkList()) {
     124          215 :         IF_ASSERT_FAILED(linkedObj) {
     125            0 :             continue;
     126              :         }
     127              : 
     128          215 :         Staff* linkedStaff = toStaff(linkedObj);
     129          215 :         Measure* linkedMeasure = linkedObj == staff ? measure : linkedStaff->score()->tick2measureMM(measure->tick());
     130          215 :         if (!linkedMeasure) {
     131            0 :             continue;
     132              :         }
     133              : 
     134          215 :         Segment* segment = linkedMeasure->getSegmentR(SegmentType::TimeTick, relTick);
     135          215 :         track_idx_t track = staff2track(linkedStaff->idx());
     136          215 :         EngravingItem* element = segment->element(track);
     137          215 :         TimeTickAnchor* anchor = element ? toTimeTickAnchor(element) : nullptr;
     138          215 :         if (!anchor) {
     139          213 :             anchor = Factory::createTimeTickAnchor(segment);
     140          213 :             anchor->setTrack(track);
     141          213 :             segment->add(anchor);
     142              :         }
     143          215 :         if (linkedMeasure == measure) {
     144          212 :             returnAnchor = anchor;
     145              :         }
     146          204 :     }
     147              : 
     148          204 :     return returnAnchor;
     149              : }
     150              : 
     151          164 : void EditTimeTickAnchors::updateLayout(Measure* measure)
     152              : {
     153          164 :     measure->computeTicks();
     154              : 
     155          164 :     Score* score = measure->score();
     156          164 :     LayoutContext ctx(score);
     157          164 :     MeasureLayout::layoutTimeTickAnchors(measure, ctx);
     158          164 : }
     159              : 
     160            0 : void MoveElementAnchors::moveElementAnchors(EngravingItem* element, KeyboardKey key, KeyboardModifier mod)
     161              : {
     162            0 :     EngravingObject* owner = element->ownershipParent();
     163            0 :     Segment* segment = owner && owner->isSegment() ? toSegment(owner) : nullptr;
     164            0 :     if (!segment) {
     165            0 :         return;
     166              :     }
     167              : 
     168            0 :     bool altMod = mod & AltModifier;
     169              : 
     170            0 :     bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool();
     171            0 :     bool changeAnchorType = altMod && canAnchorToEndOfPrevious(element);
     172            0 :     bool resetAnchorType = !altMod && anchorToEndOfPrevious;
     173            0 :     if (changeAnchorType || resetAnchorType) {
     174            0 :         element->undoChangeProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS, !anchorToEndOfPrevious,
     175            0 :                                     element->propertyFlags(Pid::ANCHOR_TO_END_OF_PREVIOUS));
     176              : 
     177            0 :         bool needMoveSeg = (key == Key_Left && anchorToEndOfPrevious) || (key == Key_Right && !anchorToEndOfPrevious);
     178            0 :         if (!needMoveSeg) {
     179            0 :             checkMeasureBoundariesAndMoveIfNeed(element);
     180            0 :             return;
     181              :         }
     182              :     }
     183              : 
     184            0 :     moveSegment(element, /*forward*/ key == Key_Right);
     185            0 :     EditTimeTickAnchors::updateAnchors(element);
     186            0 :     checkMeasureBoundariesAndMoveIfNeed(element);
     187              : }
     188              : 
     189            0 : bool MoveElementAnchors::canAnchorToEndOfPrevious(const EngravingItem* element)
     190              : {
     191            0 :     switch (element->type()) {
     192            0 :     case ElementType::HARMONY:
     193              :     case ElementType::FRET_DIAGRAM:
     194              :     case ElementType::REHEARSAL_MARK:
     195            0 :         return false;
     196            0 :     default:
     197            0 :         return true;
     198              :     }
     199              : }
     200              : 
     201          296 : void MoveElementAnchors::checkMeasureBoundariesAndMoveIfNeed(EngravingItem* element)
     202              : {
     203          296 :     Segment* curSeg = toSegment(element->parent());
     204          296 :     Fraction curTick = curSeg->tick();
     205          296 :     Measure* curMeasure = curSeg->measure();
     206          296 :     Measure* prevMeasure = curMeasure->prevMeasure();
     207          296 :     bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool();
     208          296 :     bool needMoveToNext = curTick == curMeasure->endTick() && !anchorToEndOfPrevious;
     209          296 :     bool needMoveToPrevious = curSeg->rtick().isZero() && anchorToEndOfPrevious && prevMeasure;
     210              : 
     211          296 :     if (!needMoveToPrevious && !needMoveToNext) {
     212          294 :         return;
     213              :     }
     214              : 
     215            2 :     Segment* newSeg = nullptr;
     216            2 :     if (needMoveToPrevious) {
     217            0 :         newSeg = prevMeasure->findSegment(SegmentType::TimeTick, curSeg->tick());
     218            0 :         if (!newSeg) {
     219            0 :             TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(prevMeasure, curTick - prevMeasure->tick(),
     220              :                                                                                element->staffIdx());
     221            0 :             EditTimeTickAnchors::updateLayout(prevMeasure);
     222            0 :             newSeg = anchor->segment();
     223              :         }
     224              :     } else {
     225            2 :         newSeg = curSeg->next1(SegmentType::ChordRest);
     226              :     }
     227              : 
     228            2 :     if (newSeg && newSeg->tick() == curTick) {
     229            0 :         moveSegment(element, newSeg, Fraction(0, 1));
     230              :     }
     231              : }
     232              : 
     233            0 : void MoveElementAnchors::moveElementAnchorsOnDrag(EngravingItem* element, EditData& ed)
     234              : {
     235            0 :     EngravingObject* owner = element->ownershipParent();
     236            0 :     Segment* segment = owner && owner->isSegment() ? toSegment(owner) : nullptr;
     237            0 :     if (!segment) {
     238            0 :         return;
     239              :     }
     240              : 
     241            0 :     KeyboardModifiers km = ed.modifiers;
     242            0 :     if (km & (ShiftModifier | ControlModifier)) {
     243            0 :         return;
     244              :     }
     245              : 
     246            0 :     EditTimeTickAnchors::updateAnchors(element);
     247              : 
     248            0 :     Segment* newSeg = findNewAnchorableSegmentFromDrag(element, segment);
     249              : 
     250            0 :     if (newSeg && (newSeg != segment && !newSeg->measure()->isMMRest())) {
     251            0 :         PointF curOffset = element->offset();
     252            0 :         moveSegment(element, newSeg, newSeg->tick() - segment->tick());
     253            0 :         rebaseOffsetOnMoveSegment(element, curOffset, newSeg, segment);
     254              :     }
     255              : }
     256              : 
     257            0 : Segment* MoveElementAnchors::findNewAnchorableSegmentFromDrag(EngravingItem* element, Segment* curSeg)
     258              : {
     259            0 :     const System* system = curSeg->system();
     260            0 :     if (!system) {
     261            0 :         return nullptr;
     262              :     }
     263              : 
     264            0 :     const Measure* newMeasure = nullptr;
     265            0 :     double xRef = element->canvasX() - system->canvasX();
     266            0 :     for (MeasureBase* mb : system->measures()) {
     267            0 :         if (!mb->isMeasure()) {
     268            0 :             continue;
     269              :         }
     270            0 :         if (mb->x() + mb->width() > xRef) {
     271            0 :             newMeasure = toMeasure(mb);
     272            0 :             break;
     273              :         }
     274              :     }
     275            0 :     if (!newMeasure) {
     276            0 :         return nullptr;
     277              :     }
     278              : 
     279            0 :     Segment* newSeg = nullptr;
     280            0 :     dragPositionToSegment(element->canvasPos(), newMeasure, element->staffIdx(), &newSeg, 0.5, true);
     281              : 
     282            0 :     return newSeg;
     283              : }
     284              : 
     285            0 : Segment* MoveElementAnchors::findNewAnchorSegmentForLine(LineSegment* lineSegment, const EditData& ed, const Segment* curSeg)
     286              : {
     287            0 :     SLine* line = lineSegment->line();
     288            0 :     Score* score = lineSegment->score();
     289            0 :     if (!line->allowTimeAnchor()) {
     290            0 :         if (ed.key == Key_Left) {
     291            0 :             return curSeg->prev1WithElemsOnStaff(lineSegment->staffIdx());
     292              :         }
     293            0 :         if (ed.key == Key_Right) {
     294            0 :             Segment* lastCRSegInScore = score->lastSegment();
     295            0 :             while (lastCRSegInScore && !lastCRSegInScore->isChordRestType()) {
     296            0 :                 lastCRSegInScore = lastCRSegInScore->prev1(SegmentType::ChordRest);
     297              :             }
     298            0 :             if (curSeg == lastCRSegInScore && curSeg == line->endSegment()) {
     299              :                 // If we reach this point, it means that the line in question does not accept time anchors
     300              :                 // and that the line's end segment is the last CR segment in the score. Trying to use
     301              :                 // next1WithElemsOnStaff won't do anything from here, but the last segment in the score is
     302              :                 // still a valid new anchor segment for this line (see also LineSegment::edit)...
     303            0 :                 return score->lastSegment();
     304              :             }
     305            0 :             return curSeg->next1WithElemsOnStaff(lineSegment->staffIdx());
     306              :         }
     307              :     }
     308              : 
     309            0 :     switch (ed.key) {
     310            0 :     case Key_Left: {
     311            0 :         if (ed.modifiers & ControlModifier) {
     312            0 :             Measure* m = curSeg->rtick().isZero() ? curSeg->measure()->prevMeasure() : curSeg->measure();
     313            0 :             return m ? m->findFirstR(SegmentType::ChordRest, Fraction(0, 1)) : nullptr;
     314              :         }
     315            0 :         return findNewAnchorableSegment(curSeg, /*forward*/ false);
     316              :     }
     317            0 :     case Key_Right: {
     318            0 :         if (ed.modifiers & ControlModifier) {
     319            0 :             if (Measure* measure = curSeg->measure()->nextMeasure()) {
     320            0 :                 return measure->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
     321              :             } else {
     322            0 :                 Measure* m = score->lastMeasure();
     323            0 :                 EditTimeTickAnchors::updateAnchors(m, lineSegment->staffIdx());
     324            0 :                 return m->getChordRestOrTimeTickSegment(m->endTick());
     325              :             }
     326              :         }
     327            0 :         return findNewAnchorableSegment(curSeg, /*forward*/ true);
     328              :     }
     329            0 :     case Key_Home: {
     330            0 :         Measure* m = curSeg->measure()->system()->firstMeasure();
     331            0 :         if (curSeg->rtick() == Fraction(0, 1) && m == curSeg->measure() && m->prevMeasure()) {
     332            0 :             m = m->prevMeasure()->system()->firstMeasure();
     333              :         }
     334            0 :         return m->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
     335              :     }
     336            0 :     case Key_End: {
     337            0 :         Measure* measure = curSeg->measure()->system()->lastMeasure()->nextMeasure();
     338            0 :         if (measure) {
     339            0 :             return measure->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
     340              :         } else {
     341            0 :             Measure* m = score->lastMeasure();
     342            0 :             EditTimeTickAnchors::updateAnchors(m, lineSegment->staffIdx());
     343            0 :             return m->getChordRestOrTimeTickSegment(m->endTick());
     344              :         }
     345              :     }
     346              :     }
     347              : 
     348            0 :     return nullptr;
     349              : }
     350              : 
     351            0 : void MoveElementAnchors::moveSegment(EngravingItem* element, bool forward)
     352              : {
     353            0 :     Segment* curSeg = toSegment(element->ownershipParent());
     354            0 :     Segment* newSeg = getNewSegment(element, curSeg, forward);
     355              : 
     356            0 :     if (newSeg) {
     357            0 :         moveSegment(element, newSeg, newSeg->tick() - curSeg->tick());
     358              :     }
     359            0 : }
     360              : 
     361            0 : Segment* MoveElementAnchors::getNewSegment(EngravingItem* element, Segment* curSeg, bool forward)
     362              : {
     363            0 :     switch (element->type()) {
     364            0 :     case ElementType::REHEARSAL_MARK:
     365              :     {
     366            0 :         Measure* measure = curSeg->measure();
     367            0 :         Measure* newMeasure = forward ? measure->nextMeasureMM() : measure->prevMeasureMM();
     368            0 :         return newMeasure ? newMeasure->first(SegmentType::ChordRest) : nullptr;
     369              :     }
     370            0 :     default:
     371            0 :         return findNewAnchorableSegment(curSeg, forward);
     372              :     }
     373              : }
     374              : 
     375            0 : Segment* MoveElementAnchors::findNewAnchorableSegment(const Segment* curSeg, bool forward)
     376              : {
     377            0 :     Segment* newSeg = forward ? curSeg->next1(SegmentType::Duration) : curSeg->prev1(SegmentType::Duration);
     378              : 
     379              :     // Continue until we get to a different tick than where we are
     380            0 :     while (newSeg && newSeg->tick() == curSeg->tick()) {
     381            0 :         newSeg = forward ? newSeg->next1(SegmentType::Duration) : newSeg->prev1(SegmentType::Duration);
     382              :     }
     383            0 :     if (!newSeg) {
     384            0 :         return nullptr;
     385              :     }
     386              : 
     387              :     // Always prefer ChordRest segment if one exists at the same tick
     388            0 :     if (newSeg->segmentType() != SegmentType::ChordRest) {
     389            0 :         Segment* newChordRestSeg = forward ? newSeg->next1(SegmentType::ChordRest) : newSeg->prev1(SegmentType::ChordRest);
     390            0 :         if (newChordRestSeg && newChordRestSeg->tick() == newSeg->tick()) {
     391            0 :             newSeg = newChordRestSeg;
     392              :         }
     393              :     }
     394              : 
     395            0 :     return newSeg;
     396              : }
     397              : 
     398            0 : void MoveElementAnchors::moveSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
     399              : {
     400            0 :     switch (element->type()) {
     401            0 :     case ElementType::FIGURED_BASS:
     402            0 :         doMoveSegment(toFiguredBass(element), newSeg, tickDiff);
     403            0 :         break;
     404            0 :     case ElementType::HARMONY:
     405              :     case ElementType::FRET_DIAGRAM:
     406            0 :         doMoveHarmonyOrFretDiagramSegment(element, newSeg, tickDiff);
     407            0 :         break;
     408            0 :     default:
     409            0 :         doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
     410            0 :         break;
     411              :     }
     412            0 : }
     413              : 
     414            0 : void MoveElementAnchors::doMoveSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
     415              : {
     416            0 :     IF_ASSERT_FAILED(element->parent() && element->parent()->isSegment()) {
     417            0 :         return;
     418              :     }
     419              : 
     420              :     // NOTE: when creating mmRests, we clone text elements from the underlying measure onto the
     421              :     // mmRest measure. This creates lots of additional linked copies which are hard to manage
     422              :     // and can result in duplicates. Here we need to remove copies on mmRests because they are invalidated
     423              :     // when moving segments (and if needed will be recreated at the next layout). In future we need to
     424              :     // change approach and *move* elements onto the mmRests, not clone them. [M.S.]
     425              : 
     426            0 :     Score* score = element->score();
     427            0 :     Measure* parentMeasure = toMeasure(element->findAncestor(ElementType::MEASURE));
     428            0 :     bool parentMeasureIsMMRest = parentMeasure && parentMeasure->isMMRest();
     429              : 
     430            0 :     std::list<EngravingObject*> linkedElements = element->linkList();
     431            0 :     for (EngravingObject* linkedElement : linkedElements) {
     432            0 :         if (linkedElement == element) {
     433            0 :             continue;
     434              :         }
     435            0 :         Measure* linkedParentMeasure = toMeasure(toEngravingItem(linkedElement)->findAncestor(ElementType::MEASURE));
     436            0 :         bool linkedParentMeasureIsMMrest = linkedParentMeasure && linkedParentMeasure->isMMRest();
     437            0 :         if (linkedParentMeasureIsMMrest != parentMeasureIsMMRest) {
     438            0 :             for (EngravingObject* child : linkedElement->children()) {
     439              :                 // e.g. fret diagram should also remove the harmony
     440            0 :                 child->undoUnlink();
     441            0 :                 score->undoRemoveElement(toEngravingItem(child));
     442              :             }
     443            0 :             linkedElement->undoUnlink();
     444            0 :             score->undoRemoveElement(static_cast<EngravingItem*>(linkedElement));
     445              :         }
     446              :     }
     447              : 
     448            0 :     score->undoChangeParent(element, newSeg, element->staffIdx());
     449            0 :     moveSnappedItems(element, newSeg, tickDiff);
     450            0 : }
     451              : 
     452            0 : void MoveElementAnchors::doMoveSegment(FiguredBass* element, Segment* newSeg, Fraction tickDiff)
     453              : {
     454            0 :     IF_ASSERT_FAILED(element->parent() && element->parent()->isSegment()) {
     455            0 :         return;
     456              :     }
     457              : 
     458            0 :     Segment* oldSeg = element->segment();
     459              : 
     460            0 :     doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
     461              : 
     462            0 :     track_idx_t startTrack = staff2track(element->staffIdx());
     463            0 :     track_idx_t endTrack = startTrack + VOICES;
     464              : 
     465              :     // Shorten this if needed
     466            0 :     if (newSeg->tick() > oldSeg->tick()) {
     467            0 :         FiguredBass* nextFB = nullptr;
     468            0 :         Fraction endTick = newSeg->tick() + element->ticks();
     469            0 :         for (Segment* seg = newSeg->next1(SegmentType::Duration); seg && seg->tick() <= endTick;
     470            0 :              seg = seg->next1(SegmentType::Duration)) {
     471            0 :             nextFB = toFiguredBass(seg->findAnnotation(ElementType::FIGURED_BASS, startTrack, endTrack));
     472            0 :             if (nextFB) {
     473            0 :                 break;
     474              :             }
     475              :         }
     476            0 :         if (nextFB) {
     477            0 :             element->setTicks(nextFB->tick() - newSeg->tick());
     478              :         }
     479              :     }
     480              : 
     481              :     // Shorten previous if needed
     482            0 :     if (newSeg->tick() < oldSeg->tick()) {
     483            0 :         FiguredBass* prevFB = nullptr;
     484            0 :         for (Segment* seg = newSeg->prev1(SegmentType::Duration); seg && seg->measure()->isAfterOrEqual(newSeg->measure());
     485            0 :              seg = seg->prev1(SegmentType::Duration)) {
     486            0 :             prevFB = (FiguredBass*)(seg->findAnnotation(ElementType::FIGURED_BASS, startTrack, endTrack));
     487            0 :             if (prevFB) {
     488            0 :                 break;
     489              :             }
     490              :         }
     491            0 :         if (prevFB) {
     492            0 :             prevFB->setTicks(std::min(prevFB->ticks(), newSeg->tick() - prevFB->tick()));
     493              :         }
     494              :     }
     495              : }
     496              : 
     497            0 : void MoveElementAnchors::doMoveHarmonyOrFretDiagramSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
     498              : {
     499            0 :     for (EngravingObject* item : element->linkList()) {
     500            0 :         Score* score = item->score();
     501            0 :         Measure* measure = score->tick2measure(newSeg->tick());
     502            0 :         Segment* chordRestSeg = measure->undoGetSegment(SegmentType::ChordRest, newSeg->tick());
     503            0 :         if (item == element) {
     504            0 :             newSeg = chordRestSeg;
     505              :         }
     506            0 :     }
     507              : 
     508            0 :     Fraction oldTick = element->tick();
     509              : 
     510            0 :     doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
     511              : 
     512            0 :     for (EngravingObject* item : element->linkList()) {
     513            0 :         Score* score = item->score();
     514            0 :         Segment* oldSegment = score->tick2segment(oldTick, true, SegmentType::ChordRest, false);
     515            0 :         if (oldSegment) {
     516            0 :             oldSegment->checkEmpty();
     517            0 :             if (oldSegment->empty()) {
     518            0 :                 score->undoRemoveElement(oldSegment);
     519              :             }
     520              :         }
     521            0 :     }
     522            0 : }
     523              : 
     524            0 : void MoveElementAnchors::moveSnappedItems(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
     525              : {
     526            0 :     if (EngravingItem* itemAfter = element->mutldata()->itemSnappedAfter()) {
     527            0 :         if (itemAfter->isTextBase() && itemAfter->parent() != newSeg) {
     528            0 :             element->score()->undoChangeParent(itemAfter, newSeg, itemAfter->staffIdx());
     529            0 :             moveSnappedItems(itemAfter, newSeg, tickDiff);
     530            0 :         } else if (itemAfter->isTextLineBaseSegment()) {
     531            0 :             TextLineBase* textLine = ((TextLineBaseSegment*)itemAfter)->textLineBase();
     532            0 :             if (textLine->tick() != newSeg->tick()) {
     533            0 :                 textLine->undoMoveStart(tickDiff);
     534              :             }
     535              :         }
     536              :     }
     537              : 
     538            0 :     if (EngravingItem* itemBefore = element->mutldata()->itemSnappedBefore()) {
     539            0 :         if (itemBefore->isTextBase() && itemBefore->parent() != newSeg) {
     540            0 :             element->score()->undoChangeParent(itemBefore, newSeg, itemBefore->staffIdx());
     541            0 :             moveSnappedItems(itemBefore, newSeg, tickDiff);
     542            0 :         } else if (itemBefore->isTextLineBaseSegment()) {
     543            0 :             TextLineBase* textLine = ((TextLineBaseSegment*)itemBefore)->textLineBase();
     544            0 :             if (textLine->tick2() != newSeg->tick()) {
     545            0 :                 textLine->undoMoveEnd(tickDiff);
     546              :             }
     547              :         }
     548              :     }
     549            0 : }
     550              : 
     551            0 : void MoveElementAnchors::rebaseOffsetOnMoveSegment(EngravingItem* element, const PointF& curOffset, Segment* newSeg, Segment* oldSeg)
     552              : {
     553            0 :     PointF offsetShift = newSeg->pagePos() - oldSeg->pagePos();
     554            0 :     element->setOffset(curOffset - offsetShift);
     555              : 
     556            0 :     if (EngravingItem* itemBefore = element->mutldata()->itemSnappedBefore()) {
     557            0 :         if (itemBefore->isTextBase()) {
     558            0 :             itemBefore->setOffset(itemBefore->offset() - offsetShift);
     559              :         }
     560              :     }
     561              : 
     562            0 :     if (EngravingItem* itemAfter = element->mutldata()->itemSnappedAfter()) {
     563            0 :         if (itemAfter->isTextBase()) {
     564            0 :             itemAfter->setOffset(itemAfter->offset() - offsetShift);
     565              :         }
     566              :     }
     567            0 : }
     568              : 
     569              : /********************************************
     570              :  * TimeTickAnchor
     571              :  * *****************************************/
     572              : 
     573          213 : TimeTickAnchor::TimeTickAnchor(DummyParentOr<Segment> parent)
     574            0 :     : EngravingItem(ElementType::TIME_TICK_ANCHOR, parent,
     575              :                     ElementFlag::ON_STAFF
     576            0 :                     | ElementFlag::NOT_SELECTABLE
     577          213 :                     | ElementFlag::GENERATED)
     578              : {
     579          213 :     setZ(-INT_MAX); // Make sure it is behind everything
     580          213 : }
     581              : 
     582            0 : TimeTickAnchor::DrawRegion TimeTickAnchor::drawRegion() const
     583              : {
     584            0 :     const ShowAnchors& showAnchors = score()->showAnchors();
     585            0 :     const staff_idx_t thisStaffIdx = staffIdx();
     586            0 :     const Fraction thisTick = segment()->tick();
     587              : 
     588            0 :     const bool trackOutOfRange = thisStaffIdx < showAnchors.staffIdx || thisStaffIdx >= showAnchors.endStaffIdx;
     589            0 :     const bool tickOutOfRange = thisTick < showAnchors.startTickExtendedRegion || thisTick >= showAnchors.endTickExtendedRegion;
     590            0 :     if (trackOutOfRange || tickOutOfRange) {
     591            0 :         return DrawRegion::OUT_OF_RANGE;
     592              :     }
     593              : 
     594            0 :     if (thisTick < showAnchors.startTickMainRegion || thisTick >= showAnchors.endTickMainRegion) {
     595            0 :         return DrawRegion::EXTENDED_REGION;
     596              :     }
     597              : 
     598            0 :     return DrawRegion::MAIN_REGION;
     599              : }
     600              : 
     601            0 : voice_idx_t TimeTickAnchor::voiceIdx() const
     602              : {
     603            0 :     return score()->showAnchors().voiceIdx;
     604              : }
     605              : } // namespace mu::engraving
        

Generated by: LCOV version 2.0-1