LCOV - code coverage report
Current view: top level - src/engraving/editing - editstaffbrackets.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 71.5 % 151 108
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 "editstaffbrackets.h"
      24              : 
      25              : #include "../dom/bracket.h"
      26              : #include "../dom/bracketitem.h"
      27              : #include "../dom/factory.h"
      28              : #include "../dom/score.h"
      29              : #include "../dom/staff.h"
      30              : 
      31              : using namespace mu::engraving;
      32              : 
      33           66 : void EditStaffBrackets::undoAddBracket(Score* score, Staff* staff, size_t level, BracketType type, size_t span)
      34              : {
      35           66 :     staff_idx_t startStaffIdx = staff->idx();
      36           66 :     staff_idx_t totStaves = score->nstaves();
      37              : 
      38              :     // Make sure this brackets won't overlap with others sharing same column.
      39              :     // If overlaps are found, move the other brackets outwards (i.e. increase column).
      40          171 :     for (staff_idx_t staffIdx = startStaffIdx; staffIdx < startStaffIdx + span && staffIdx < totStaves; ++staffIdx) {
      41          105 :         const std::vector<BracketItem*>& scoreBrackets = score->brackets(staffIdx);
      42              : 
      43          105 :         bool collision = false;
      44          172 :         for (BracketItem* b : scoreBrackets) {
      45           82 :             if (b->bracketType() != BracketType::NO_BRACKET && b->bracketType() != BracketType::GROUP
      46           82 :                 && b->column() == level) {
      47            0 :                 collision = true;
      48            0 :                 break;
      49              :             }
      50              :         }
      51              : 
      52          105 :         if (!collision) {
      53          105 :             continue;
      54              :         }
      55              : 
      56            0 :         for (int i = static_cast<int>(scoreBrackets.size()) - 1; i >= static_cast<int>(level); --i) {
      57            0 :             if (i >= static_cast<int>(scoreBrackets.size())) {
      58              :                 // This might theoretically happen when a lot of brackets get cleaned up
      59              :                 // after changing the column of the first bracket we see
      60            0 :                 continue;
      61              :             }
      62              : 
      63            0 :             BracketItem* bi = scoreBrackets[i];
      64            0 :             if (bi->column() > level) {
      65            0 :                 continue;
      66              :             }
      67              : 
      68            0 :             if (bi->bracketType() == BracketType::NO_BRACKET || bi->bracketType() == BracketType::GROUP) {
      69            0 :                 continue;
      70              :             }
      71              : 
      72            0 :             bi->undoChangeProperty(Pid::BRACKET_COLUMN, bi->column() + 1);
      73              :         }
      74              :     }
      75              : 
      76           66 :     score->undo(new AddBracket(staff, level, type, span));
      77           66 : }
      78              : 
      79            0 : void EditStaffBrackets::undoRemoveBracket(Score* score, Bracket* b)
      80              : {
      81            0 :     score->undo(new RemoveBracket(b->staff(), b->column(), b->bracketType(), b->span()));
      82            0 : }
      83              : 
      84         1418 : void EditStaffBrackets::fillBrackets(Score* score, staff_idx_t staffIdx, size_t idx)
      85              : {
      86              :     // make sure index idx is valid
      87         1418 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
      88         2234 :     for (size_t i = staffBrackets.size(); i <= idx; ++i) {
      89          816 :         BracketItem* bi = Factory::createBracketItem(score->dummy());
      90          816 :         bi->setStartStaffIdx(staffIdx);
      91          816 :         bi->setColumn(i);
      92          816 :         bi->setScore(score);
      93          816 :         staffBrackets.push_back(bi);
      94              :     }
      95         1418 : }
      96              : 
      97          631 : void EditStaffBrackets::cleanBrackets(Score* score, staff_idx_t staffIdx)
      98              : {
      99              :     // remove NO_BRACKET entries from the end of list
     100          631 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     101         1440 :     while (!staffBrackets.empty() && (staffBrackets.back()->bracketType() == BracketType::NO_BRACKET)) {
     102          178 :         BracketItem* bi = muse::takeLast(staffBrackets);
     103          178 :         delete bi;
     104              :     }
     105          631 : }
     106              : 
     107           12 : void EditStaffBrackets::adjustBracketsDel(Score* score, size_t sidx, size_t eidx)
     108              : {
     109           12 :     IF_ASSERT_FAILED(sidx < eidx && eidx <= score->staves().size()) {
     110            0 :         return;
     111              :     }
     112              : 
     113           27 :     for (size_t staffIdx = 0; staffIdx < eidx; ++staffIdx) {
     114           15 :         const std::vector<BracketItem*> staffBrackets = score->brackets(staffIdx);
     115           28 :         for (BracketItem* bi : staffBrackets) {
     116           13 :             size_t span = bi->bracketSpan();
     117           13 :             if ((span == 0) || ((staffIdx + span) <= sidx)) {
     118            1 :                 continue;
     119              :             }
     120           12 :             const bool startsOutsideDeletedRange = (staffIdx < sidx);
     121           12 :             const bool endsOutsideDeletedRange = ((staffIdx + span) >= eidx);
     122           12 :             if (startsOutsideDeletedRange && endsOutsideDeletedRange) {
     123              :                 // Shorten the bracket by the number of staves deleted
     124            3 :                 bi->undoChangeProperty(Pid::BRACKET_SPAN, int(span - (eidx - sidx)));
     125            9 :             } else if (startsOutsideDeletedRange) {
     126              :                 // Shorten the bracket by the number of staves deleted that were spanned by it
     127            0 :                 bi->undoChangeProperty(Pid::BRACKET_SPAN, int(sidx - staffIdx));
     128            9 :             } else if (endsOutsideDeletedRange) {
     129            9 :                 const size_t column = bi->column();
     130            9 :                 const BracketType bracketType = bi->bracketType();
     131            9 :                 score->undo(new RemoveBracket(score->staves().at(staffIdx), bi->column(), bi->bracketType(), span));
     132            9 :                 int newSpan = int(span - (eidx - staffIdx));
     133            9 :                 if (eidx < score->staves().size() && newSpan > 0) {
     134              :                     // Move the bracket past the end of the deleted range,
     135              :                     // and shorten it by the number of staves deleted that were spanned by it.
     136              : 
     137            6 :                     EditStaffBrackets::undoAddBracket(score, score->staves().at(eidx), column, bracketType, newSpan);
     138              :                 }
     139              :             }
     140              :         }
     141           15 :     }
     142              : }
     143              : 
     144            0 : void EditStaffBrackets::adjustBracketsIns(Score* score, size_t sidx, size_t eidx)
     145              : {
     146            0 :     for (size_t staffIdx = 0; staffIdx < score->staves().size(); ++staffIdx) {
     147            0 :         const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     148            0 :         for (BracketItem* bi : staffBrackets) {
     149            0 :             size_t span = bi->bracketSpan();
     150            0 :             if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx)) {
     151            0 :                 continue;
     152              :             }
     153            0 :             if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) {
     154            0 :                 bi->undoChangeProperty(Pid::BRACKET_SPAN, int(span + (eidx - sidx)));
     155              :             }
     156              :         }
     157              :     }
     158            0 : }
     159              : 
     160          631 : void EditStaffBrackets::setBracketType(Score* score, staff_idx_t staffIdx, size_t idx, BracketType val)
     161              : {
     162          631 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     163              : 
     164          631 :     fillBrackets(score, staffIdx, idx);
     165          631 :     staffBrackets[idx]->setBracketType(val);
     166          631 :     cleanBrackets(score, staffIdx);
     167          631 : }
     168              : 
     169            0 : void EditStaffBrackets::changeBracketColumn(Score* score, staff_idx_t staffIdx, size_t oldColumn, size_t newColumn)
     170              : {
     171            0 :     if (oldColumn == newColumn) {
     172            0 :         return;
     173              :     }
     174            0 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     175              : 
     176            0 :     size_t idx = std::max(oldColumn, newColumn);
     177            0 :     fillBrackets(score, staffIdx, idx);
     178            0 :     int step = newColumn > oldColumn ? 1 : -1;
     179            0 :     for (size_t i = oldColumn; i != newColumn; i += step) {
     180            0 :         size_t oldIdx = i;
     181            0 :         size_t newIdx = i + step;
     182            0 :         staffBrackets[oldIdx]->setColumn(newIdx);
     183            0 :         staffBrackets[newIdx]->setColumn(oldIdx);
     184            0 :         muse::swapItemsAt(staffBrackets, oldIdx, newIdx);
     185              :     }
     186            0 :     cleanBrackets(score, staffIdx);
     187              : }
     188              : 
     189          549 : void EditStaffBrackets::setBracketSpan(Score* score, staff_idx_t staffIdx, size_t idx, size_t val)
     190              : {
     191          549 :     const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     192          549 :     fillBrackets(score, staffIdx, idx);
     193          549 :     staffBrackets[idx]->setBracketSpan(val);
     194          549 : }
     195              : 
     196          233 : void EditStaffBrackets::setBracketVisible(Score* score, staff_idx_t staffIdx, size_t idx, bool v)
     197              : {
     198          233 :     const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     199          233 :     fillBrackets(score, staffIdx, idx);
     200          233 :     staffBrackets[idx]->setVisible(v);
     201          233 : }
     202              : 
     203           34 : void EditStaffBrackets::addBracket(Score* score, staff_idx_t staffIdx, BracketItem* b)
     204              : {
     205           34 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     206           34 :     b->setStartStaffIdx(staffIdx);
     207           34 :     if (!staffBrackets.empty() && staffBrackets[0]->bracketType() == BracketType::NO_BRACKET) {
     208            6 :         delete staffBrackets[0];
     209            6 :         staffBrackets[0] = b;
     210              :     } else {
     211              :         //
     212              :         // create new bracket level
     213              :         //
     214          113 :         for (staff_idx_t sidx = 0; sidx < score->nstaves(); ++sidx) {
     215           85 :             if (sidx == staffIdx) {
     216           28 :                 staffBrackets.push_back(b);
     217           28 :                 b->setScore(score);
     218              :             } else {
     219           57 :                 BracketItem* bi = Factory::createBracketItem(score->dummy());
     220           57 :                 bi->setStartStaffIdx(sidx);
     221           57 :                 bi->setScore(score);
     222           57 :                 score->brackets(sidx).push_back(bi);
     223              :             }
     224              :         }
     225              :     }
     226           34 : }
     227              : 
     228           38 : void EditStaffBrackets::insertBracket(Score* score, staff_idx_t staffIdx, BracketItem* b)
     229              : {
     230           38 :     std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
     231           38 :     b->setStartStaffIdx(staffIdx);
     232           38 :     size_t column = b->column();
     233           38 :     if (column < staffBrackets.size()) {
     234            0 :         if (staffBrackets[column]) {
     235            0 :             delete staffBrackets[column];
     236              :         }
     237            0 :         staffBrackets[column] = b;
     238           38 :     } else if (column == staffBrackets.size()) {
     239           33 :         staffBrackets.push_back(b);
     240              :     } else {
     241            5 :         fillBrackets(score, staffIdx, column - 1);
     242            5 :         staffBrackets.push_back(b);
     243              :     }
     244           38 : }
     245              : 
     246              : //---------------------------------------------------------
     247              : //   AddBracket
     248              : //---------------------------------------------------------
     249              : 
     250           71 : void AddBracket::redo()
     251              : {
     252           71 :     EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, bracketType);
     253           71 :     EditStaffBrackets::setBracketSpan(staff->score(), staff->idx(), level, span);
     254           71 :     staff->triggerLayout();
     255           71 : }
     256              : 
     257           38 : void AddBracket::undo()
     258              : {
     259           38 :     EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, BracketType::NO_BRACKET);
     260           38 :     staff->triggerLayout();
     261           38 : }
     262              : 
     263              : //---------------------------------------------------------
     264              : //   RemoveBracket
     265              : //---------------------------------------------------------
     266              : 
     267           44 : void RemoveBracket::redo()
     268              : {
     269           44 :     EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, BracketType::NO_BRACKET);
     270           44 :     staff->triggerLayout();
     271           44 : }
     272              : 
     273           27 : void RemoveBracket::undo()
     274              : {
     275           27 :     EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, bracketType);
     276           27 :     EditStaffBrackets::setBracketSpan(staff->score(), staff->idx(), level, span);
     277           27 :     staff->triggerLayout();
     278           27 : }
        

Generated by: LCOV version 2.0-1