LCOV - code coverage report
Current view: top level - src/engraving/rw/read410 - readcontext.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 70.9 % 227 161
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 "readcontext.h"
      24              : 
      25              : #include "dom/linkedobjects.h"
      26              : #include "dom/measure.h"
      27              : #include "dom/score.h"
      28              : #include "dom/staff.h"
      29              : #include "dom/trill.h"
      30              : 
      31              : #include "connectorinforeader.h"
      32              : 
      33              : #include "log.h"
      34              : 
      35              : using namespace mu::engraving;
      36              : using namespace mu::engraving::read410;
      37              : 
      38          186 : ReadContext::ReadContext(Score* score)
      39          186 :     : m_score(score)
      40              : {
      41          186 : }
      42              : 
      43          186 : ReadContext::~ReadContext()
      44              : {
      45          186 :     if (!_connectors.empty() || !_pendingConnectors.empty()) {
      46            0 :         LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
      47            0 :         for (auto& c : _connectors) {
      48            0 :             EngravingItem* conn = c->releaseConnector();
      49            0 :             if (conn && !conn->isTuplet()) { // tuplets are added to score even when not finished
      50            0 :                 delete conn;
      51              :             }
      52              :         }
      53            0 :         for (auto& c : _pendingConnectors) {
      54            0 :             delete c->releaseConnector();
      55              :         }
      56              :     }
      57          186 : }
      58              : 
      59         2963 : Score* ReadContext::score() const
      60              : {
      61         2963 :     return m_score;
      62              : }
      63              : 
      64            2 : void ReadContext::setScore(Score* score)
      65              : {
      66            2 :     m_score = score;
      67            2 : }
      68              : 
      69          237 : const MStyle& ReadContext::style() const
      70              : {
      71          237 :     return score()->style();
      72              : }
      73              : 
      74            0 : std::shared_ptr<IEngravingFontsProvider> ReadContext::engravingFonts() const
      75              : {
      76            0 :     return score()->engravingFonts();
      77              : }
      78              : 
      79            0 : String ReadContext::mscoreVersion() const
      80              : {
      81            0 :     return m_score->mscoreVersion();
      82              : }
      83              : 
      84            0 : int ReadContext::mscVersion() const
      85              : {
      86            0 :     return m_score->mscVersion();
      87              : }
      88              : 
      89            0 : int ReadContext::fileDivision() const
      90              : {
      91            0 :     return m_score->fileDivision();
      92              : }
      93              : 
      94            0 : int ReadContext::fileDivision(int t) const
      95              : {
      96            0 :     return m_score->fileDivision(t);
      97              : }
      98              : 
      99          578 : double ReadContext::spatium() const
     100              : {
     101          578 :     return m_score->style().spatium();
     102              : }
     103              : 
     104            0 : void ReadContext::setSpatium(double v)
     105              : {
     106            0 :     m_score->style().set(Sid::spatium, v);
     107            0 : }
     108              : 
     109         2819 : DummyParent* ReadContext::dummy() const
     110              : {
     111         2819 :     return m_score->dummy();
     112              : }
     113              : 
     114         1797 : Staff* ReadContext::staff(staff_idx_t n)
     115              : {
     116         1797 :     return m_score->staff(n);
     117              : }
     118              : 
     119         1737 : bool ReadContext::isSameScore(const EngravingObject* obj) const
     120              : {
     121         1737 :     return obj->score() == m_score;
     122              : }
     123              : 
     124          186 : rw::ReadLinks ReadContext::readLinks() const
     125              : {
     126          186 :     rw::ReadLinks l;
     127          186 :     l.linksIndexer = m_linksIndexer;
     128          186 :     l.staffLinkedElements = m_staffLinkedElements;
     129          186 :     return l;
     130            0 : }
     131              : 
     132            9 : void ReadContext::initLinks(const rw::ReadLinks& l)
     133              : {
     134            9 :     m_linksIndexer = l.linksIndexer;
     135            9 :     m_staffLinkedElements = l.staffLinkedElements;
     136            9 : }
     137              : 
     138          577 : void ReadContext::addLink(Staff* staff, LinkedObjects* link, const Location& location)
     139              : {
     140          577 :     int staffIndex = static_cast<int>(staff->idx());
     141          577 :     const bool isMasterScore = staff->score()->isMaster();
     142          577 :     if (!isMasterScore) {
     143            0 :         staffIndex *= -1;
     144              :     }
     145              : 
     146          577 :     std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
     147          577 :     if (!isMasterScore) {
     148            0 :         if (!staffLinks.empty()
     149            0 :             && (link->mainElement()->score() != staffLinks.front().first->mainElement()->score())
     150              :             ) {
     151            0 :             staffLinks.clear();
     152              :         }
     153              :     }
     154              : 
     155          577 :     m_linksIndexer.assignLocalIndex(location);
     156          577 :     staffLinks.push_back(std::make_pair(link, location));
     157          577 : }
     158              : 
     159          486 : LinkedObjects* ReadContext::getLink(bool isMasterScore, const Location& location, int localIndexDiff)
     160              : {
     161          486 :     int staffIndex = location.staff();
     162          486 :     if (!isMasterScore) {
     163            4 :         staffIndex *= -1;
     164              :     }
     165              : 
     166          486 :     const int localIndex = m_linksIndexer.assignLocalIndex(location) + localIndexDiff;
     167          486 :     std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
     168              : 
     169          486 :     if (!staffLinks.empty() && staffLinks.back().second == location) {
     170              :         // This element potentially affects local index for "main"
     171              :         // elements that may go afterwards at the same tick, so
     172              :         // append it to staffLinks as well.
     173           55 :         staffLinks.push_back(staffLinks.back()); // nothing should reference exactly this local index, so it shouldn't matter what to append
     174              :     }
     175              : 
     176         4618 :     for (size_t i = 0; i < staffLinks.size(); ++i) {
     177         4617 :         if (staffLinks[i].second == location) {
     178          485 :             if (localIndex == 0) {
     179          290 :                 return staffLinks[i].first;
     180              :             }
     181              : 
     182          195 :             i += localIndex;
     183          195 :             if (i >= staffLinks.size()) {
     184            0 :                 return nullptr;
     185              :             }
     186              : 
     187          195 :             if (staffLinks[i].second == location) {
     188          195 :                 return staffLinks[i].first;
     189              :             }
     190              : 
     191            0 :             return nullptr;
     192              :         }
     193              :     }
     194              : 
     195            1 :     return nullptr;
     196              : }
     197              : 
     198          540 : Fraction ReadContext::rtick() const
     199              : {
     200          540 :     return _curMeasure ? _tick - _curMeasure->tick() : _tick;
     201              : }
     202              : 
     203         3871 : void ReadContext::setTick(const Fraction& f)
     204              : {
     205         3871 :     _tick = f.reduced();
     206         3871 :     _intTick = _tick.ticks();
     207         3871 : }
     208              : 
     209         3446 : void ReadContext::incTick(const Fraction& f)
     210              : {
     211         3446 :     _tick += f;
     212         3446 :     _tick.reduce();
     213         3446 :     _intTick += f.ticks();
     214         3446 : }
     215              : 
     216         1094 : Location ReadContext::location(bool forceAbsFrac) const
     217              : {
     218         1094 :     Location l = Location::absolute();
     219         1094 :     fillLocation(l, forceAbsFrac);
     220         1094 :     return l;
     221              : }
     222              : 
     223              : //---------------------------------------------------------
     224              : //   fillLocation
     225              : //    fills location fields which have default values with
     226              : //    values relevant for the current reader's position.
     227              : //    When in paste mode (or forceAbsFrac is true) absolute
     228              : //    fraction values are used and measure number is set to
     229              : //    zero.
     230              : //---------------------------------------------------------
     231              : 
     232         1603 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
     233              : {
     234         1603 :     constexpr Location defaults = Location::absolute();
     235         1603 :     const bool absFrac = (pasteMode() || forceAbsFrac);
     236         1603 :     if (l.track() == defaults.track()) {
     237         1094 :         l.setTrack(static_cast<int>(track()));
     238              :     }
     239         1603 :     if (l.frac() == defaults.frac()) {
     240         1603 :         l.setFrac(absFrac ? tick() : rtick());
     241              :     }
     242         1603 :     if (l.measure() == defaults.measure()) {
     243         1603 :         l.setMeasure(absFrac ? 0 : currentMeasureIndex());
     244              :     }
     245         1603 : }
     246              : 
     247              : //---------------------------------------------------------
     248              : //   setLocation
     249              : //    sets a new reading location, taking into account its
     250              : //    type (absolute or relative).
     251              : //---------------------------------------------------------
     252              : 
     253           62 : void ReadContext::setLocation(const Location& l)
     254              : {
     255           62 :     if (l.isRelative()) {
     256           31 :         Location newLoc = l;
     257           31 :         newLoc.toAbsolute(location());
     258           31 :         int intTicks = l.frac().ticks();
     259           31 :         if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
     260            0 :             _intTick += intTicks;
     261            0 :             setTrack(newLoc.track() - _trackOffset);
     262            0 :             return;
     263              :         }
     264           31 :         setLocation(newLoc); // recursion
     265           31 :         return;
     266              :     }
     267           31 :     setTrack(l.track() - _trackOffset);
     268           31 :     setTick(l.frac() - _tickOffset);
     269           31 :     if (!pasteMode()) {
     270           31 :         assert(l.measure() == currentMeasureIndex());
     271           31 :         incTick(currentMeasure()->tick());
     272              :     }
     273              : }
     274              : 
     275          254 : void ReadContext::removeConnector(const ConnectorInfoReader* c)
     276              : {
     277          256 :     while (c->prev()) {
     278            2 :         c = c->prev();
     279              :     }
     280          762 :     while (c) {
     281          508 :         ConnectorInfoReader* next = c->next();
     282          676 :         for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
     283          676 :             if (it->get() == c) {
     284          508 :                 _connectors.erase(it);
     285          508 :                 break;
     286              :             }
     287              :         }
     288          508 :         c = next;
     289              :     }
     290          254 : }
     291              : 
     292          509 : void ReadContext::addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c)
     293              : {
     294          509 :     _pendingConnectors.push_back(c);
     295          509 : }
     296              : 
     297         1737 : void ReadContext::checkConnectors()
     298              : {
     299         2246 :     for (std::shared_ptr<ConnectorInfoReader>& c : _pendingConnectors) {
     300          509 :         addConnectorInfo(c);
     301              :     }
     302         1737 :     _pendingConnectors.clear();
     303         1737 : }
     304              : 
     305          509 : void ReadContext::addConnectorInfo(std::shared_ptr<ConnectorInfoReader> c)
     306              : {
     307          509 :     _connectors.push_back(std::move(c));
     308          509 :     ConnectorInfoReader* c1 = _connectors.back().get();
     309          509 :     c1->update();
     310          930 :     for (std::shared_ptr<ConnectorInfoReader>& c2 : _connectors) {
     311          675 :         if (c2->connect(c1)) {
     312          254 :             if (c2->finished()) {
     313          254 :                 c2->addToScore(pasteMode());
     314          254 :                 removeConnector(c2.get());
     315              :             }
     316          254 :             break;
     317              :         }
     318              :     }
     319          509 : }
     320              : 
     321            0 : static bool distanceSort(const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p1,
     322              :                          const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p2)
     323              : {
     324            0 :     return p1.first < p2.first;
     325              : }
     326              : 
     327              : //---------------------------------------------------------
     328              : //   reconnectBrokenConnectors
     329              : //---------------------------------------------------------
     330              : 
     331          187 : void ReadContext::reconnectBrokenConnectors()
     332              : {
     333          187 :     if (_connectors.empty()) {
     334          186 :         return;
     335              :     }
     336            1 :     LOGD("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
     337            1 :     std::vector<std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> > > brokenPairs;
     338            1 :     for (size_t i = 1; i < _connectors.size(); ++i) {
     339            0 :         for (size_t j = 0; j < i; ++j) {
     340            0 :             ConnectorInfoReader* c1 = _connectors[i].get();
     341            0 :             ConnectorInfoReader* c2 = _connectors[j].get();
     342            0 :             int d = c1->connectionDistance(*c2);
     343            0 :             if (d >= 0) {
     344            0 :                 brokenPairs.push_back(std::make_pair(d, std::make_pair(c1, c2)));
     345              :             } else {
     346            0 :                 brokenPairs.push_back(std::make_pair(-d, std::make_pair(c2, c1)));
     347              :             }
     348              :         }
     349              :     }
     350            1 :     std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
     351            1 :     std::set<ConnectorInfoReader*> processed;
     352            1 :     for (auto& distPair : brokenPairs) {
     353            0 :         if (distPair.first == INT_MAX) {
     354            0 :             continue;
     355              :         }
     356            0 :         auto& pair = distPair.second;
     357            0 :         if (processed.count(pair.first) || processed.count(pair.second)) {
     358            0 :             continue;
     359              :         }
     360            0 :         pair.first->forceConnect(pair.second);
     361            0 :         processed.insert(pair.first);
     362            0 :         processed.insert(pair.second);
     363              :     }
     364            1 :     std::set<ConnectorInfoReader*> reconnected;
     365            2 :     for (auto& conn : _connectors) {
     366            1 :         ConnectorInfoReader* c = conn.get();
     367            1 :         if (c->finished()) {
     368            0 :             reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
     369              :         }
     370              :     }
     371            1 :     for (ConnectorInfoReader* cptr : reconnected) {
     372            0 :         cptr->addToScore(pasteMode());
     373            0 :         removeConnector(cptr);
     374              :     }
     375            1 :     LOGD() << "reconnected broken connectors: " << reconnected.size();
     376            1 : }
     377              : 
     378          186 : void ReadContext::clearOrphanedConnectors()
     379              : {
     380          186 :     if (_connectors.empty() && _pendingConnectors.empty()) {
     381          185 :         return;
     382              :     }
     383              : 
     384            1 :     LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
     385              : 
     386            1 :     std::set<LinkedObjects*> deletedLinks;
     387              : 
     388            1 :     auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
     389            1 :         EngravingItem* conn = c ? c->releaseConnector() : nullptr;
     390            1 :         if (!conn) {
     391            1 :             return;
     392              :         }
     393              : 
     394            0 :         LinkedObjects* links = conn->links();
     395            0 :         bool linksWillBeDeleted = links && links->size() == 1;
     396              : 
     397            0 :         if (!conn->isTuplet()) {     // tuplets are added to score even when not finished
     398            0 :             if (linksWillBeDeleted) {
     399            0 :                 deletedLinks.insert(links);
     400            0 :                 if (conn->isTrill()) {
     401            0 :                     EngravingItem* ornament = (EngravingItem*)toTrill(conn)->ornament();
     402            0 :                     if (ornament && ornament->links()) {
     403            0 :                         deletedLinks.insert(ornament->links());
     404              :                     }
     405              :                 }
     406              :             }
     407              : 
     408            0 :             delete conn;
     409              :         }
     410            1 :     };
     411              : 
     412            1 :     if (!_connectors.empty()) {
     413            2 :         for (auto& c : _connectors) {
     414            1 :             deleteConnectors(c);
     415              :         }
     416            1 :         _connectors.clear();
     417              :     }
     418              : 
     419            1 :     if (!_pendingConnectors.empty()) {
     420            0 :         for (auto& c : _pendingConnectors) {
     421            0 :             deleteConnectors(c);
     422              :         }
     423            0 :         _pendingConnectors.clear();
     424              :     }
     425              : 
     426            1 :     for (auto& it : m_staffLinkedElements) {
     427            0 :         std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
     428            0 :         muse::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
     429            0 :             return deletedLinks.count(pair.first);
     430              :         });
     431              :     }
     432            1 : }
     433              : 
     434          321 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
     435              : {
     436          321 :     if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
     437          235 :         _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
     438              :     }
     439          321 : }
        

Generated by: LCOV version 2.0-1