LCOV - code coverage report
Current view: top level - src/engraving/editing - editpagelocks.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 74.7 % 297 222
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) 2026 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 "editpagelocks.h"
      24              : #include "editsystemlocks.h"
      25              : 
      26              : #include "transaction/transaction.h"
      27              : #include "transaction/undoablecommand.h"
      28              : 
      29              : #include "../dom/measure.h"
      30              : #include "../dom/measurebase.h"
      31              : #include "../dom/page.h"
      32              : #include "../dom/pagelockindicator.h"
      33              : #include "../dom/rangelock.h"
      34              : #include "../dom/score.h"
      35              : #include "../dom/system.h"
      36              : 
      37              : using namespace mu::engraving;
      38              : 
      39              : //---------------------------------------------------------
      40              : //   AddPageLock
      41              : //---------------------------------------------------------
      42              : 
      43              : class AddPageLock : public UndoableCommand
      44              : {
      45           36 :     OBJECT_ALLOCATOR(engraving, AddPageLock)
      46              : 
      47              :     const RangeLock* m_pageLock;
      48              : public:
      49           18 :     AddPageLock(const RangeLock* pageLock)
      50           18 :         : m_pageLock(pageLock) {}
      51              : 
      52            0 :     void undo() override
      53              :     {
      54            0 :         Score* score = m_pageLock->startMB()->score();
      55            0 :         score->removePageLock(m_pageLock);
      56            0 :     }
      57              : 
      58           18 :     void redo() override
      59              :     {
      60           18 :         Score* score = m_pageLock->startMB()->score();
      61           18 :         score->addPageLock(m_pageLock);
      62           18 :     }
      63              : 
      64           18 :     void cleanup(bool undo) override
      65              :     {
      66           18 :         if (!undo) {
      67            0 :             delete m_pageLock;
      68            0 :             m_pageLock = nullptr;
      69              :         }
      70           18 :     }
      71              : 
      72            0 :     UNDO_NAME("AddPageLock")
      73              : 
      74           18 :     std::vector<EngravingObject*> objectItems() const override
      75              :     {
      76           54 :         return { m_pageLock->startMB(), m_pageLock->endMB() };
      77              :     }
      78              : };
      79              : 
      80              : //---------------------------------------------------------
      81              : //   RemovePageLock
      82              : //---------------------------------------------------------
      83              : 
      84              : class RemovePageLock : public UndoableCommand
      85              : {
      86           46 :     OBJECT_ALLOCATOR(engraving, RemovePageLock)
      87              : 
      88              :     const RangeLock* m_pageLock;
      89              : public:
      90           23 :     RemovePageLock(const RangeLock* pageLock)
      91           23 :         : m_pageLock(pageLock) {}
      92              : 
      93            0 :     void undo() override
      94              :     {
      95            0 :         Score* score = m_pageLock->startMB()->score();
      96            0 :         score->addPageLock(m_pageLock);
      97            0 :     }
      98              : 
      99           23 :     void redo() override
     100              :     {
     101           23 :         Score* score = m_pageLock->startMB()->score();
     102           23 :         score->removePageLock(m_pageLock);
     103           23 :     }
     104              : 
     105           23 :     void cleanup(bool undo) override
     106              :     {
     107           23 :         if (undo) {
     108           23 :             delete m_pageLock;
     109           23 :             m_pageLock = nullptr;
     110              :         }
     111           23 :     }
     112              : 
     113            0 :     UNDO_NAME("RemovePageLock")
     114           23 :     std::vector<EngravingObject*> objectItems() const override
     115              :     {
     116           69 :         return { m_pageLock->startMB(), m_pageLock->endMB() };
     117              :     }
     118              : };
     119              : 
     120              : //---------------------------------------------------------
     121              : //   EditPageLocks
     122              : //---------------------------------------------------------
     123              : 
     124           18 : void EditPageLocks::undoAddPageLock(Transaction& tx, const RangeLock* lock)
     125              : {
     126           18 :     removeLayoutBreaksOnAddPageLock(tx, lock);
     127           18 :     tx.push(new AddPageLock(lock));
     128           18 : }
     129              : 
     130           23 : void EditPageLocks::undoRemovePageLock(Transaction& tx, const RangeLock* lock)
     131              : {
     132           23 :     tx.push(new RemovePageLock(lock));
     133           23 : }
     134              : 
     135            1 : void EditPageLocks::undoRemoveAllLocks(Transaction& tx, Score* score)
     136              : {
     137            1 :     std::vector<const RangeLock*> allLocks = score->pageLocks()->allLocks(); // copy
     138            2 :     for (const RangeLock* lock : allLocks) {
     139            1 :         undoRemovePageLock(tx, lock);
     140              :     }
     141            1 : }
     142              : 
     143            2 : void EditPageLocks::togglePageLock(Transaction& tx, Score* score, const std::vector<Page*>& pages)
     144              : {
     145            2 :     bool unlockAll = true;
     146            3 :     for (const Page* page : pages) {
     147            2 :         if (!page->isLocked()) {
     148            1 :             unlockAll = false;
     149            1 :             break;
     150              :         }
     151              :     }
     152              : 
     153            4 :     for (Page* page : pages) {
     154            2 :         MeasureBase* startMeas = page->firstMeasureBase();
     155            2 :         const RangeLock* currentLock = score->pageLocks()->lockStartingAt(startMeas);
     156            2 :         if (currentLock && unlockAll) {
     157            1 :             undoRemovePageLock(tx, currentLock);
     158            1 :             continue;
     159            1 :         } else if (!currentLock && !unlockAll) {
     160            1 :             RangeLock* newPageLock = new RangeLock(startMeas, page->lastMeasureBase());
     161            1 :             undoAddPageLock(tx, newPageLock);
     162              :         }
     163              :     }
     164            2 : }
     165              : 
     166            3 : void EditPageLocks::toggleScoreLock(Transaction& tx, Score* score)
     167              : {
     168            3 :     bool unlockAll = true;
     169            7 :     for (const Page* page : score->pages()) {
     170            6 :         if (!page->isLocked()) {
     171            2 :             unlockAll = false;
     172            2 :             break;
     173              :         }
     174              :     }
     175              : 
     176            9 :     for (Page* page : score->pages()) {
     177            6 :         MeasureBase* startMeas = page->firstMeasureBase();
     178            6 :         const RangeLock* currentLock = score->pageLocks()->lockStartingAt(startMeas);
     179            6 :         if (currentLock && unlockAll) {
     180            4 :             undoRemovePageLock(tx, currentLock);
     181            4 :             continue;
     182            2 :         } else if (!currentLock && !unlockAll) {
     183            2 :             RangeLock* newPageLock = new RangeLock(startMeas, page->lastMeasureBase());
     184            2 :             undoAddPageLock(tx, newPageLock);
     185              :         }
     186              :     }
     187            3 : }
     188              : 
     189            3 : void EditPageLocks::addRemovePageLocks(Transaction& tx, Score* score, int interval, bool lock)
     190              : {
     191              :     // Should only be called on range selection of whole score
     192            3 :     MeasureBase* startMeasure = score->selection().startMeasureBase();
     193            3 :     MeasureBase* endMeasure = score->selection().endMeasureBase();
     194            3 :     if (!endMeasure) {
     195            0 :         endMeasure = score->lastMeasureMM();
     196              :     }
     197              : 
     198            3 :     if (!startMeasure || !endMeasure) {
     199            2 :         return;
     200              :     }
     201              : 
     202            3 :     if (lock) {
     203            2 :         for (const Page* page : score->pages()) {
     204            1 :             if (page->lastMeasureBase()->isBefore(startMeasure)) {
     205            0 :                 continue;
     206              :             }
     207            1 :             if (page->firstMeasureBase()->isAfter(endMeasure)) {
     208            0 :                 break;
     209              :             }
     210            1 :             if (!page->isLocked()) {
     211            1 :                 undoAddPageLock(tx, new RangeLock(page->firstMeasureBase(), page->lastMeasureBase()));
     212              :             }
     213              :         }
     214            1 :         return;
     215              :     }
     216              : 
     217            2 :     std::vector<const RangeLock*> currentLocks = score->pageLocks()->locksContainedInRange(startMeasure, endMeasure);
     218            7 :     for (const RangeLock* l : currentLocks) {
     219            5 :         undoRemovePageLock(tx, l);
     220              :     }
     221              : 
     222            2 :     if (interval == 0) {
     223            1 :         return;
     224              :     }
     225              : 
     226            1 :     int count = 0;
     227            1 :     MeasureBase* lockStart = nullptr;
     228           16 :     for (MeasureBase* mb = startMeasure; mb; mb = mb->nextMM()) {
     229           16 :         if (count == 0) {
     230            4 :             lockStart = mb;
     231              :         }
     232           16 :         count++;
     233           16 :         if (count == interval || mb == endMeasure) {
     234            4 :             undoAddPageLock(tx, new RangeLock(lockStart, mb));
     235            4 :             lockStart = nullptr;
     236            4 :             count = 0;
     237              :         }
     238           16 :         if (mb == endMeasure) {
     239            1 :             break;
     240              :         }
     241              :     }
     242            2 : }
     243              : 
     244            2 : void EditPageLocks::makeIntoPage(Transaction& tx, Score* score, MeasureBase* first, MeasureBase* last)
     245              : {
     246            2 :     const RangeLock* lockContainingFirst = score->pageLocks()->lockContaining(first);
     247            2 :     const RangeLock* lockContainingLast = score->pageLocks()->lockContaining(last);
     248              : 
     249            2 :     if (lockContainingFirst) {
     250            1 :         undoRemovePageLock(tx, lockContainingFirst);
     251            1 :         if (lockContainingFirst->startMB()->isBefore(first)) {
     252            1 :             MeasureBase* oneBeforeFirst = first->prevMM();
     253            1 :             RangeLock* newLockBefore = new RangeLock(lockContainingFirst->startMB(), oneBeforeFirst);
     254            1 :             undoAddPageLock(tx, newLockBefore);
     255              :         }
     256              :     }
     257              : 
     258            2 :     if (lockContainingLast) {
     259            1 :         if (lockContainingLast != lockContainingFirst) {
     260            1 :             undoRemovePageLock(tx, lockContainingLast);
     261              :         }
     262            1 :         if (last->isBefore(lockContainingLast->endMB())) {
     263            1 :             MeasureBase* oneAfterLast = last->nextMM();
     264            1 :             RangeLock* newLockAfter = new RangeLock(oneAfterLast, lockContainingLast->endMB());
     265            1 :             undoAddPageLock(tx, newLockAfter);
     266              :         }
     267              :     }
     268              : 
     269            2 :     std::vector<const RangeLock*> locksContainedInRange = score->pageLocks()->locksContainedInRange(first, last);
     270            2 :     for (const RangeLock* lock : locksContainedInRange) {
     271            0 :         if (lock != lockContainingFirst && lock != lockContainingLast) {
     272            0 :             undoRemovePageLock(tx, lock);
     273              :         }
     274              :     }
     275              : 
     276            2 :     RangeLock* newLock = new RangeLock(first, last);
     277            2 :     undoAddPageLock(tx, newLock);
     278            2 : }
     279              : 
     280            1 : void EditPageLocks::moveMeasuresToPrevPage(Transaction& tx, Score* score, MeasureBase* first, MeasureBase* last)
     281              : {
     282            1 :     const Page* prevPage = last->prevPage();
     283            1 :     if (!prevPage) {
     284            0 :         return;
     285              :     }
     286              : 
     287              :     // Lock systems we are moving to the previous page
     288            1 :     std::vector<System*> systemsToLock;
     289            1 :     System* curSystem = nullptr;
     290            2 :     for (MeasureBase* curMeasure = last; curMeasure && curMeasure != first->prev(); curMeasure = curMeasure->prev()) {
     291            1 :         if (curMeasure->system() == curSystem) {
     292            0 :             continue;
     293              :         }
     294            1 :         curSystem = curMeasure->system();
     295              : 
     296            1 :         if (!curSystem->isLocked()) {
     297            1 :             systemsToLock.push_back(curSystem);
     298              :         }
     299              :     }
     300            1 :     EditSystemLocks::toggleSystemLock(tx, score, systemsToLock);
     301              : 
     302            1 :     MeasureBase* prevPageFirstMeas = prevPage->firstMeasureBase();
     303              : 
     304            1 :     const RangeLock* prevPageLock = score->pageLocks()->lockStartingAt(prevPageFirstMeas);
     305            1 :     if (prevPageLock) {
     306            1 :         undoRemovePageLock(tx, prevPageLock);
     307              :     }
     308              : 
     309            1 :     const Page* curPage = last->page();
     310            1 :     const RangeLock* curPageLock = score->pageLocks()->lockStartingAt(curPage->firstMeasureBase());
     311            1 :     if (curPageLock) {
     312            1 :         undoRemovePageLock(tx, curPageLock);
     313            1 :         if (curPageLock->endMB() != last) {
     314            1 :             MeasureBase* nextMB = last->nextMM();
     315            1 :             RangeLock* newLockOnCurPage = new RangeLock(nextMB, curPageLock->endMB());
     316            1 :             undoAddPageLock(tx, newLockOnCurPage);
     317              :         }
     318              :     }
     319              : 
     320            1 :     RangeLock* pageLock = new RangeLock(prevPageFirstMeas, last);
     321            1 :     undoAddPageLock(tx, pageLock);
     322            1 : }
     323              : 
     324            1 : void EditPageLocks::moveMeasuresToNextPage(Transaction& tx, Score* score, MeasureBase* first, MeasureBase* last)
     325              : {
     326            1 :     const Page* curPage = first->page();
     327            1 :     MeasureBase* startMeas = curPage->firstMeasureBase();
     328            1 :     bool refMeasureIsStartOfPage = first == startMeas;
     329              : 
     330            1 :     const Page* nextPage = first->nextPage();
     331            1 :     if (!nextPage) {
     332            0 :         return;
     333              :     }
     334              : 
     335              :     // Lock systems we are moving to the next page
     336            1 :     std::vector<System*> systemsToLock;
     337            1 :     System* curSystem = nullptr;
     338            2 :     for (MeasureBase* curMeasure = first; curMeasure && curMeasure != last->next();
     339            1 :          curMeasure = curMeasure->next()) {
     340            1 :         if (curMeasure->system() == curSystem) {
     341            0 :             continue;
     342              :         }
     343            1 :         curSystem = curMeasure->system();
     344              : 
     345            1 :         if (!curSystem->isLocked()) {
     346            1 :             systemsToLock.push_back(curSystem);
     347              :         }
     348              :     }
     349            1 :     EditSystemLocks::toggleSystemLock(tx, score, systemsToLock);
     350              : 
     351            1 :     const RangeLock* curLock = score->pageLocks()->lockStartingAt(startMeas);
     352            1 :     if (curLock) {
     353            1 :         undoRemovePageLock(tx, curLock);
     354              :     }
     355              : 
     356            1 :     if (!refMeasureIsStartOfPage) {
     357            1 :         MeasureBase* prevMeas = first->prevMM();
     358            1 :         if (!curLock) {
     359            0 :             prevMeas->undoSetBreak(true, LayoutBreakType::PAGE);
     360              :         } else {
     361            1 :             RangeLock* pageLock = new RangeLock(startMeas, prevMeas);
     362            1 :             undoAddPageLock(tx, pageLock);
     363              :         }
     364              :     }
     365              : 
     366            1 :     const RangeLock* nextPageLock = score->pageLocks()->lockStartingAt(nextPage->firstMeasureBase());
     367            1 :     if (nextPageLock) {
     368            1 :         undoRemovePageLock(tx, nextPageLock);
     369              :     }
     370              : 
     371            1 :     if (nextPageLock || refMeasureIsStartOfPage) {
     372            1 :         RangeLock* newNextPageLock = new RangeLock(first, nextPage->lastMeasureBase());
     373            1 :         undoAddPageLock(tx, newNextPageLock);
     374              :     }
     375            1 : }
     376              : 
     377            1 : void EditPageLocks::applyLockToSelection(Transaction& tx, Score* score)
     378              : {
     379            1 :     MeasureBase* first = nullptr;
     380            1 :     MeasureBase* last = nullptr;
     381              : 
     382            1 :     if (score->selection().isRange()) {
     383            1 :         first = score->selection().startMeasureBase();
     384            1 :         last = score->selection().endMeasureBase();
     385              :     } else {
     386            0 :         for (EngravingItem* el : score->selection().elements()) {
     387            0 :             if (el->isPageLockIndicator()) {
     388            0 :                 const RangeLock* lock = toPageLockIndicator(el)->pageLock();
     389            0 :                 first = lock->startMB();
     390            0 :                 last = lock->endMB();
     391            0 :                 break;
     392              :             }
     393            0 :             MeasureBase* mb = el->findMeasureBase();
     394            0 :             if (!mb) {
     395            0 :                 continue;
     396              :             }
     397            0 :             if (!first || mb->isBefore(first)) {
     398            0 :                 first = mb;
     399              :             }
     400            0 :             if (!last || mb->isAfter(last)) {
     401            0 :                 last = mb;
     402              :             }
     403              :         }
     404              :     }
     405              : 
     406            1 :     if (!first || !last) {
     407            0 :         return;
     408              :     }
     409              : 
     410            1 :     const RangeLock* lockOnLast = score->pageLocks()->lockContaining(last);
     411            1 :     if (lockOnLast && lockOnLast->endMB() == last) {
     412            0 :         undoRemovePageLock(tx, lockOnLast);
     413            1 :     } else if (first != last) {
     414            1 :         makeIntoPage(tx, score, first, last);
     415              :     } else {
     416            0 :         makeIntoPage(tx, score, first->page()->firstMeasureBase(), last);
     417              :     }
     418              : }
     419              : 
     420            0 : void EditPageLocks::removePageLocksOnAddLayoutBreak(Transaction& tx, Score* score, LayoutBreakType breakType, const MeasureBase* measure)
     421              : {
     422            0 :     IF_ASSERT_FAILED(breakType != LayoutBreakType::NOBREAK) {
     423            0 :         return; // NOBREAK not allowed on locked measures
     424              :     }
     425              : 
     426            0 :     const RangeLock* lock = score->pageLocks()->lockContaining(measure);
     427            0 :     MeasureBase* lockEndMeasure = lock ? lock->endMB() : nullptr;
     428            0 :     if (lock && breakType == LayoutBreakType::PAGE) {
     429            0 :         undoRemovePageLock(tx, lock);
     430              : 
     431            0 :         if (measure != lockEndMeasure && measure->next()) {
     432              :             // Make sure the resultant page is locked
     433            0 :             undoAddPageLock(tx, new RangeLock(measure->next(), lockEndMeasure));
     434              :         }
     435              :     }
     436              : }
     437              : 
     438           18 : void EditPageLocks::removeLayoutBreaksOnAddPageLock(Transaction&, const RangeLock* lock)
     439              : {
     440          112 :     for (MeasureBase* mb = lock->startMB(); mb && mb->isBeforeOrEqual(lock->endMB()); mb = mb->nextMM()) {
     441           94 :         mb->undoSetBreak(false, LayoutBreakType::PAGE);
     442              :     }
     443           18 : }
     444              : 
     445           62 : void EditPageLocks::removePageLocksOnRemoveMeasures(Transaction& tx, Score* score, const MeasureBase* m1, const MeasureBase* m2)
     446              : {
     447           62 :     std::vector<const RangeLock*> allPageLocks = score->pageLocks()->allLocks();
     448           62 :     for (const RangeLock* lock : allPageLocks) {
     449            0 :         MeasureBase* lockStart = lock->startMB();
     450            0 :         MeasureBase* lockEnd = lock->endMB();
     451            0 :         bool lockStartIsInRange = lockStart->isAfterOrEqual(m1) && lockStart->isBeforeOrEqual(m2);
     452            0 :         bool lockEndIsInRange = lockEnd->isAfterOrEqual(m1) && lockEnd->isBeforeOrEqual(m2);
     453            0 :         if (lockStartIsInRange || lockEndIsInRange) {
     454            0 :             undoRemovePageLock(tx, lock);
     455              :         }
     456            0 :         if (lockStartIsInRange && !lockEndIsInRange) {
     457            0 :             MeasureBase* newLockStart = m2->nextMeasure();
     458            0 :             if (newLockStart) {
     459            0 :                 undoAddPageLock(tx, new RangeLock(newLockStart, lockEnd));
     460              :             }
     461            0 :         } else if (!lockStartIsInRange && lockEndIsInRange) {
     462            0 :             MeasureBase* newLockEnd = m1->prevMeasure();
     463            0 :             if (newLockEnd) {
     464            0 :                 undoAddPageLock(tx, new RangeLock(lockStart, newLockEnd));
     465              :             }
     466              :         }
     467              :     }
     468           62 : }
     469              : 
     470            2 : void EditPageLocks::removePageLocksContainingMMRests(Transaction& tx, Score* score)
     471              : {
     472            2 :     std::vector<const RangeLock*> allLocks = score->pageLocks()->allLocks(); // copy
     473            3 :     for (const RangeLock* lock : allLocks) {
     474            1 :         if ((lock->startMB()->isMeasure() && toMeasure(lock->startMB())->isMMRest())
     475            1 :             || (lock->endMB()->isMeasure() && toMeasure(lock->endMB())->isMMRest())) {
     476            1 :             undoRemovePageLock(tx, lock);
     477            1 :             continue;
     478              :         }
     479            0 :         for (MeasureBase* mb = lock->startMB(); mb; mb = mb->next()) {
     480            0 :             if (mb->isMeasure() && toMeasure(mb)->mmRest()) {
     481            0 :                 undoRemovePageLock(tx, lock);
     482            0 :                 break;
     483              :             }
     484            0 :             if (mb->isAfter(lock->endMB())) {
     485            0 :                 break;
     486              :             }
     487              :         }
     488              :     }
     489            2 : }
     490              : 
     491          166 : void EditPageLocks::updatePageLocksOnCreateMMRests(Transaction& tx, Score* score, Measure* first, Measure* last)
     492              : {
     493              :     // NOTE: this must be done during layout as the mmRests get created.
     494              : 
     495          169 :     for (const RangeLock* lock : score->pageLocks()->locksContainedInRange(first, last)) {
     496              :         // These locks are inside the range of the mmRest so remove them
     497            3 :         undoRemovePageLock(tx, lock);
     498          166 :     }
     499              : 
     500          166 :     const RangeLock* lockOnFirst = score->pageLocks()->lockContaining(first);
     501          166 :     const RangeLock* lockOnLast = score->pageLocks()->lockContaining(last);
     502              : 
     503          166 :     if (lockOnFirst) {
     504            1 :         MeasureBase* startMB = lockOnFirst->startMB();
     505            1 :         MeasureBase* endMB = lockOnFirst->endMB();
     506              : 
     507            1 :         if (startMB->isBefore(first)) {
     508            1 :             if (endMB->isBeforeOrEqual(last)) {
     509            1 :                 endMB = first->mmRest();
     510              :             } else {
     511            0 :                 return;
     512              :             }
     513              :         } else {
     514            0 :             startMB = first->mmRest();
     515              :         }
     516              : 
     517            1 :         if (startMB != lockOnFirst->startMB() || endMB != lockOnFirst->endMB()) {
     518            1 :             undoRemovePageLock(tx, lockOnFirst);
     519            1 :             undoAddPageLock(tx, new RangeLock(startMB, endMB));
     520              :         }
     521              :     }
     522              : 
     523          166 :     if (!lockOnLast || lockOnLast == lockOnFirst) {
     524          166 :         return;
     525              :     }
     526              : 
     527            0 :     MeasureBase* startMB = lockOnLast->startMB();
     528            0 :     MeasureBase* endMB = lockOnLast->endMB();
     529            0 :     assert(startMB->isAfter(first) && endMB->isAfter(last));
     530              : 
     531            0 :     undoRemovePageLock(tx, lockOnLast);
     532            0 :     startMB = last->nextMM();
     533            0 :     undoAddPageLock(tx, new RangeLock(startMB, endMB));
     534              : }
        

Generated by: LCOV version 2.0-1