LCOV - code coverage report
Current view: top level - src/engraving/rw/read460 - readcontext.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 54.1 % 183 99
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/masterscore.h"
      27              : #include "dom/measure.h"
      28              : #include "dom/score.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::read460;
      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         2739 : Score* ReadContext::score() const
      60              : {
      61         2739 :     return m_score;
      62              : }
      63              : 
      64           12 : void ReadContext::setScore(Score* score)
      65              : {
      66           12 :     m_score = score;
      67           12 : }
      68              : 
      69            0 : const MStyle& ReadContext::style() const
      70              : {
      71            0 :     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          211 : int ReadContext::mscVersion() const
      85              : {
      86          211 :     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          760 : double ReadContext::spatium() const
     100              : {
     101          760 :     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         3798 : DummyParent* ReadContext::dummy() const
     110              : {
     111         3798 :     return m_score->dummy();
     112              : }
     113              : 
     114         1801 : Staff* ReadContext::staff(staff_idx_t n)
     115              : {
     116         1801 :     return m_score->staff(n);
     117              : }
     118              : 
     119         1695 : bool ReadContext::isSameScore(const EngravingObject* obj) const
     120              : {
     121         1695 :     return obj->score() == m_score;
     122              : }
     123              : 
     124          382 : Fraction ReadContext::rtick() const
     125              : {
     126          382 :     return _curMeasure ? _tick - _curMeasure->tick() : _tick;
     127              : }
     128              : 
     129         3713 : void ReadContext::setTick(const Fraction& f)
     130              : {
     131         3713 :     _tick = f.reduced();
     132         3713 :     _intTick = _tick.ticks();
     133         3713 : }
     134              : 
     135         4619 : void ReadContext::incTick(const Fraction& f)
     136              : {
     137         4619 :     _tick += f;
     138         4619 :     _tick.reduce();
     139         4619 :     _intTick += f.ticks();
     140         4619 : }
     141              : 
     142           50 : Location ReadContext::location(bool forceAbsFrac) const
     143              : {
     144           50 :     Location l = Location::absolute();
     145           50 :     fillLocation(l, forceAbsFrac);
     146           50 :     return l;
     147              : }
     148              : 
     149              : //---------------------------------------------------------
     150              : //   fillLocation
     151              : //    fills location fields which have default values with
     152              : //    values relevant for the current reader's position.
     153              : //    When in paste mode (or forceAbsFrac is true) absolute
     154              : //    fraction values are used and measure number is set to
     155              : //    zero.
     156              : //---------------------------------------------------------
     157              : 
     158          382 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
     159              : {
     160          382 :     constexpr Location defaults = Location::absolute();
     161          382 :     const bool absFrac = (pasteMode() || forceAbsFrac);
     162          382 :     if (l.track() == defaults.track()) {
     163           50 :         l.setTrack(static_cast<int>(track()));
     164              :     }
     165          382 :     if (l.frac() == defaults.frac()) {
     166          382 :         l.setFrac(absFrac ? tick() : rtick());
     167              :     }
     168          382 :     if (l.measure() == defaults.measure()) {
     169          382 :         l.setMeasure(absFrac ? 0 : currentMeasureIndex());
     170              :     }
     171          382 : }
     172              : 
     173              : //---------------------------------------------------------
     174              : //   setLocation
     175              : //    sets a new reading location, taking into account its
     176              : //    type (absolute or relative).
     177              : //---------------------------------------------------------
     178              : 
     179          100 : void ReadContext::setLocation(const Location& l)
     180              : {
     181          100 :     if (l.isRelative()) {
     182           50 :         Location newLoc = l;
     183           50 :         newLoc.toAbsolute(location());
     184           50 :         int intTicks = l.frac().ticks();
     185           50 :         if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
     186            0 :             _intTick += intTicks;
     187            0 :             setTrack(newLoc.track() - _trackOffset);
     188            0 :             return;
     189              :         }
     190           50 :         setLocation(newLoc); // recursion
     191           50 :         return;
     192              :     }
     193           50 :     setTrack(l.track() - _trackOffset);
     194           50 :     setTick(l.frac() - _tickOffset);
     195           50 :     if (!pasteMode()) {
     196           50 :         assert(l.measure() == currentMeasureIndex());
     197           50 :         incTick(currentMeasure()->tick());
     198              :     }
     199              : }
     200              : 
     201          166 : void ReadContext::removeConnector(const ConnectorInfoReader* c)
     202              : {
     203          190 :     while (c->prev()) {
     204           24 :         c = c->prev();
     205              :     }
     206          498 :     while (c) {
     207          332 :         ConnectorInfoReader* next = c->next();
     208          428 :         for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
     209          428 :             if (it->get() == c) {
     210          332 :                 _connectors.erase(it);
     211          332 :                 break;
     212              :             }
     213              :         }
     214          332 :         c = next;
     215              :     }
     216          166 : }
     217              : 
     218          332 : void ReadContext::addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c)
     219              : {
     220          332 :     _pendingConnectors.push_back(c);
     221          332 : }
     222              : 
     223         1695 : void ReadContext::checkConnectors()
     224              : {
     225         2027 :     for (std::shared_ptr<ConnectorInfoReader>& c : _pendingConnectors) {
     226          332 :         addConnectorInfo(c);
     227              :     }
     228         1695 :     _pendingConnectors.clear();
     229         1695 : }
     230              : 
     231          332 : void ReadContext::addConnectorInfo(std::shared_ptr<ConnectorInfoReader> c)
     232              : {
     233          332 :     _connectors.push_back(std::move(c));
     234          332 :     ConnectorInfoReader* c1 = _connectors.back().get();
     235          332 :     c1->update();
     236          570 :     for (std::shared_ptr<ConnectorInfoReader>& c2 : _connectors) {
     237          404 :         if (c2->connect(c1)) {
     238          166 :             if (c2->finished()) {
     239          166 :                 c2->addToScore(pasteMode());
     240          166 :                 removeConnector(c2.get());
     241              :             }
     242          166 :             break;
     243              :         }
     244              :     }
     245          332 : }
     246              : 
     247            0 : static bool distanceSort(const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p1,
     248              :                          const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p2)
     249              : {
     250            0 :     return p1.first < p2.first;
     251              : }
     252              : 
     253              : //---------------------------------------------------------
     254              : //   reconnectBrokenConnectors
     255              : //---------------------------------------------------------
     256              : 
     257          123 : void ReadContext::reconnectBrokenConnectors()
     258              : {
     259          123 :     if (_connectors.empty()) {
     260          123 :         return;
     261              :     }
     262            0 :     LOGD("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
     263            0 :     std::vector<std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> > > brokenPairs;
     264            0 :     for (size_t i = 1; i < _connectors.size(); ++i) {
     265            0 :         for (size_t j = 0; j < i; ++j) {
     266            0 :             ConnectorInfoReader* c1 = _connectors[i].get();
     267            0 :             ConnectorInfoReader* c2 = _connectors[j].get();
     268            0 :             int d = c1->connectionDistance(*c2);
     269            0 :             if (d >= 0) {
     270            0 :                 brokenPairs.push_back(std::make_pair(d, std::make_pair(c1, c2)));
     271              :             } else {
     272            0 :                 brokenPairs.push_back(std::make_pair(-d, std::make_pair(c2, c1)));
     273              :             }
     274              :         }
     275              :     }
     276            0 :     std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
     277            0 :     std::set<ConnectorInfoReader*> processed;
     278            0 :     for (auto& distPair : brokenPairs) {
     279            0 :         if (distPair.first == INT_MAX) {
     280            0 :             continue;
     281              :         }
     282            0 :         auto& pair = distPair.second;
     283            0 :         if (processed.count(pair.first) || processed.count(pair.second)) {
     284            0 :             continue;
     285              :         }
     286            0 :         pair.first->forceConnect(pair.second);
     287            0 :         processed.insert(pair.first);
     288            0 :         processed.insert(pair.second);
     289              :     }
     290            0 :     std::set<ConnectorInfoReader*> reconnected;
     291            0 :     for (auto& conn : _connectors) {
     292            0 :         ConnectorInfoReader* c = conn.get();
     293            0 :         if (c->finished()) {
     294            0 :             reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
     295              :         }
     296              :     }
     297            0 :     for (ConnectorInfoReader* cptr : reconnected) {
     298            0 :         cptr->addToScore(pasteMode());
     299            0 :         removeConnector(cptr);
     300              :     }
     301            0 :     LOGD() << "reconnected broken connectors: " << reconnected.size();
     302            0 : }
     303              : 
     304          117 : void ReadContext::clearOrphanedConnectors()
     305              : {
     306          117 :     if (_connectors.empty() && _pendingConnectors.empty()) {
     307          117 :         return;
     308              :     }
     309              : 
     310            0 :     LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
     311              : 
     312            0 :     std::set<LinkedObjects*> deletedLinks;
     313              : 
     314            0 :     auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
     315            0 :         EngravingItem* conn = c ? c->releaseConnector() : nullptr;
     316            0 :         if (!conn) {
     317            0 :             return;
     318              :         }
     319              : 
     320            0 :         LinkedObjects* links = conn->links();
     321            0 :         bool linksWillBeDeleted = links && links->size() == 1;
     322              : 
     323            0 :         if (!conn->isTuplet()) {     // tuplets are added to score even when not finished
     324            0 :             if (linksWillBeDeleted) {
     325            0 :                 deletedLinks.insert(links);
     326            0 :                 if (conn->isTrill()) {
     327            0 :                     EngravingItem* ornament = (EngravingItem*)toTrill(conn)->ornament();
     328            0 :                     if (ornament && ornament->links()) {
     329            0 :                         deletedLinks.insert(ornament->links());
     330              :                     }
     331              :                 }
     332              :             }
     333              : 
     334            0 :             if (conn->eid().isValid()) {
     335            0 :                 conn->masterScore()->eidRegister()->removeItem(conn);
     336              :             }
     337              : 
     338            0 :             delete conn;
     339              :         }
     340            0 :     };
     341              : 
     342            0 :     if (!_connectors.empty()) {
     343            0 :         for (auto& c : _connectors) {
     344            0 :             deleteConnectors(c);
     345              :         }
     346            0 :         _connectors.clear();
     347              :     }
     348              : 
     349            0 :     if (!_pendingConnectors.empty()) {
     350            0 :         for (auto& c : _pendingConnectors) {
     351            0 :             deleteConnectors(c);
     352              :         }
     353            0 :         _pendingConnectors.clear();
     354              :     }
     355            0 : }
     356              : 
     357          231 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
     358              : {
     359          231 :     if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
     360          153 :         _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
     361              :     }
     362          231 : }
        

Generated by: LCOV version 2.0-1