LCOV - code coverage report
Current view: top level - src/engraving/rw/read400 - connectorinforeader.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 58.7 % 218 128
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              : #include "connectorinforeader.h"
      23              : 
      24              : #include "../../infrastructure/rtti.h"
      25              : 
      26              : #include "../../dom/factory.h"
      27              : #include "../../dom/score.h"
      28              : #include "../../dom/engravingitem.h"
      29              : #include "../../dom/spanner.h"
      30              : #include "../../dom/chordrest.h"
      31              : #include "../../dom/measure.h"
      32              : #include "../../dom/note.h"
      33              : #include "../../dom/noteline.h"
      34              : #include "../../dom/tie.h"
      35              : #include "../../dom/chord.h"
      36              : #include "../../dom/staff.h"
      37              : 
      38              : #include "../types/typesconv.h"
      39              : 
      40              : #include "../xmlreader.h"
      41              : 
      42              : #include "tread.h"
      43              : 
      44              : #include "log.h"
      45              : 
      46              : using namespace mu::engraving;
      47              : using namespace mu::engraving::read400;
      48              : 
      49              : //---------------------------------------------------------
      50              : //   ConnectorInfoReader
      51              : //---------------------------------------------------------
      52              : 
      53         3062 : ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, ReadContext* ctx, EngravingItem* current, int track)
      54         3062 :     : ConnectorInfo(current, track), m_reader(&e), m_ctx(ctx), m_connector(nullptr), m_connectorReceiver(current)
      55         3062 : {}
      56              : 
      57              : //---------------------------------------------------------
      58              : //   readPositionInfo
      59              : //---------------------------------------------------------
      60              : 
      61            0 : static Location readPositionInfo(ReadContext* ctx, int track)
      62              : {
      63            0 :     Location info = ctx->location();
      64            0 :     info.setTrack(track);
      65            0 :     return info;
      66              : }
      67              : 
      68              : //---------------------------------------------------------
      69              : //   ConnectorInfoReader
      70              : //---------------------------------------------------------
      71              : 
      72            0 : ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, ReadContext* ctx, Score* current, int track)
      73            0 :     : ConnectorInfo(current, readPositionInfo(ctx, track)), m_reader(&e), m_ctx(ctx), m_connector(nullptr), m_connectorReceiver(current)
      74              : {
      75            0 :     setCurrentUpdated(true);
      76            0 : }
      77              : 
      78              : //---------------------------------------------------------
      79              : //   ConnectorInfoReader::read
      80              : //---------------------------------------------------------
      81              : 
      82         3062 : bool ConnectorInfoReader::read()
      83              : {
      84         3062 :     XmlReader& e = *m_reader;
      85         3062 :     const AsciiStringView name(e.asciiAttribute("type"));
      86         3062 :     m_type = TConv::fromXml(name, ElementType::INVALID);
      87              : 
      88         3062 :     m_ctx->fillLocation(m_currentLoc);
      89              : 
      90         7655 :     while (e.readNextStartElement()) {
      91         4593 :         const AsciiStringView tag(e.name());
      92              : 
      93         4593 :         if (tag == "prev") {
      94         1531 :             readEndpointLocation(m_prevLoc);
      95         3062 :         } else if (tag == "next") {
      96         1531 :             readEndpointLocation(m_nextLoc);
      97              :         } else {
      98         1531 :             if (tag == name) {
      99         1531 :                 m_connector = Factory::createItemByName(tag, m_connectorReceiver->score()->dummy());
     100              :             } else {
     101            0 :                 LOGW("ConnectorInfoReader::read: element tag (%s) does not match connector type (%s). Is the file corrupted?",
     102              :                      tag.ascii(), name.ascii());
     103              :             }
     104              : 
     105         1531 :             if (!m_connector) {
     106            0 :                 e.unknown();
     107            0 :                 return false;
     108              :             }
     109         1531 :             m_connector->setTrack(m_currentLoc.track());
     110         1531 :             read400::TRead::readItem(m_connector, e, *m_ctx);
     111              :         }
     112              :     }
     113         3062 :     return true;
     114              : }
     115              : 
     116              : //---------------------------------------------------------
     117              : //   ConnectorInfoReader::readEndpointLocation
     118              : //---------------------------------------------------------
     119              : 
     120         3062 : void ConnectorInfoReader::readEndpointLocation(Location& l)
     121              : {
     122         3062 :     XmlReader& e = *m_reader;
     123         6124 :     while (e.readNextStartElement()) {
     124         3062 :         const AsciiStringView tag(e.name());
     125         3062 :         if (tag == "location") {
     126         3062 :             l = Location::relative();
     127         3062 :             read400::TRead::read(&l, e, *m_ctx);
     128              :         } else {
     129            0 :             e.unknown();
     130              :         }
     131              :     }
     132         3062 : }
     133              : 
     134              : //---------------------------------------------------------
     135              : //   ConnectorInfoReader::update
     136              : //---------------------------------------------------------
     137              : 
     138         3062 : void ConnectorInfoReader::update()
     139              : {
     140         3062 :     if (!currentUpdated()) {
     141         3062 :         updateCurrentInfo(m_ctx->pasteMode());
     142              :     }
     143         3062 :     if (hasPrevious()) {
     144         1531 :         m_prevLoc.toAbsolute(m_currentLoc);
     145              :     }
     146         3062 :     if (hasNext()) {
     147         1531 :         m_nextLoc.toAbsolute(m_currentLoc);
     148              :     }
     149         3062 : }
     150              : 
     151              : //---------------------------------------------------------
     152              : //   ConnectorInfoReader::addToScore
     153              : //---------------------------------------------------------
     154              : 
     155              : class ReadAddConnectorVisitor : public rtti::Visitor<ReadAddConnectorVisitor>
     156              : {
     157              : public:
     158              : 
     159              :     template<typename T>
     160         7684 :     static bool doVisit(EngravingObject* item, ConnectorInfoReader* info, bool pasteMode)
     161              :     {
     162         7684 :         if (T::classof(item) || (std::is_same<T, ChordRest>::value && item->isChordRest())) {
     163         3062 :             ConnectorInfoReader::readAddConnector(static_cast<T*>(item), info, pasteMode);
     164         3062 :             return true;
     165              :         }
     166         4622 :         return false;
     167              :     }
     168              : };
     169              : 
     170              : using ReadAddConnectorTypes = rtti::TypeList<ChordRest, Measure, Note, Score>;
     171              : 
     172         1531 : void ConnectorInfoReader::addToScore(bool pasteMode)
     173              : {
     174         1531 :     ConnectorInfoReader* r = this;
     175         1540 :     while (r->prev()) {
     176            9 :         r = r->prev();
     177              :     }
     178         4593 :     while (r) {
     179         3062 :         bool found = ReadAddConnectorVisitor::visit(ReadAddConnectorTypes {}, r->m_connectorReceiver, r, pasteMode);
     180         3062 :         DO_ASSERT(found);
     181         3062 :         r = r->next();
     182              :     }
     183         1531 : }
     184              : 
     185              : //---------------------------------------------------------
     186              : //   ConnectorInfoReader::readConnector
     187              : //---------------------------------------------------------
     188              : 
     189         3062 : void ConnectorInfoReader::readConnector(std::shared_ptr<ConnectorInfoReader> info, XmlReader& e, ReadContext& ctx)
     190              : {
     191         3062 :     if (!info->read()) {
     192            0 :         e.skipCurrentElement();
     193            0 :         return;
     194              :     }
     195         3062 :     ctx.addConnectorInfoLater(info);
     196              : }
     197              : 
     198              : //---------------------------------------------------------
     199              : //   ConnectorInfoReader::connector
     200              : //---------------------------------------------------------
     201              : 
     202         3062 : EngravingItem* ConnectorInfoReader::connector()
     203              : {
     204              :     // connector should be contained in the first node normally.
     205         3062 :     ConnectorInfo* i = findFirst();
     206         3062 :     if (i) {
     207         3062 :         return static_cast<ConnectorInfoReader*>(i)->m_connector;
     208              :     }
     209            0 :     return nullptr;
     210              : }
     211              : 
     212              : //---------------------------------------------------------
     213              : //   ConnectorInfoReader::connector
     214              : //---------------------------------------------------------
     215              : 
     216            0 : const EngravingItem* ConnectorInfoReader::connector() const
     217              : {
     218            0 :     return const_cast<ConnectorInfoReader*>(this)->connector();
     219              : }
     220              : 
     221              : //---------------------------------------------------------
     222              : //   ConnectorInfoReader::releaseConnector
     223              : //---------------------------------------------------------
     224              : 
     225            0 : EngravingItem* ConnectorInfoReader::releaseConnector()
     226              : {
     227            0 :     ConnectorInfoReader* i = static_cast<ConnectorInfoReader*>(findFirst());
     228            0 :     if (!i) {
     229              :         // circular connector?
     230            0 :         ConnectorInfoReader* ii = this;
     231            0 :         EngravingItem* c = nullptr;
     232            0 :         while (ii->prev()) {
     233            0 :             if (ii->m_connector) {
     234            0 :                 c = ii->m_connector;
     235            0 :                 ii->m_connector = nullptr;
     236              :             }
     237            0 :             ii = ii->prev();
     238            0 :             if (ii == this) {
     239            0 :                 break;
     240              :             }
     241              :         }
     242            0 :         return c;
     243              :     }
     244            0 :     EngravingItem* c = i->m_connector;
     245            0 :     i->m_connector = nullptr;
     246            0 :     return c;
     247              : }
     248              : 
     249          420 : void ConnectorInfoReader::readAddConnector(ChordRest* item, ConnectorInfoReader* info, bool pasteMode)
     250              : {
     251          420 :     const ElementType type = info->type();
     252          420 :     switch (type) {
     253          420 :     case ElementType::SLUR:
     254              :     {
     255          420 :         Spanner* spanner = toSpanner(info->connector());
     256          420 :         const Location& l = info->location();
     257              : 
     258          420 :         if (info->isStart()) {
     259          210 :             spanner->setTrack(l.track());
     260          210 :             spanner->setTick(item->tick());
     261          210 :             spanner->setStartElement(item);
     262          210 :             if (pasteMode) {
     263            0 :                 item->score()->undoAddElement(spanner);
     264            0 :                 for (EngravingObject* ee : spanner->linkList()) {
     265            0 :                     if (ee == spanner) {
     266            0 :                         continue;
     267              :                     }
     268            0 :                     Spanner* ls = toSpanner(ee);
     269            0 :                     ls->setTick(spanner->tick());
     270            0 :                     for (EngravingObject* eee : item->linkList()) {
     271            0 :                         ChordRest* cr = toChordRest(eee);
     272            0 :                         if (cr->score() == eee->score() && cr->staffIdx() == ls->staffIdx()) {
     273            0 :                             ls->setTrack(cr->track());
     274            0 :                             if (ls->isSlur()) {
     275            0 :                                 ls->setStartElement(cr);
     276              :                             }
     277            0 :                             break;
     278              :                         }
     279            0 :                     }
     280            0 :                 }
     281              :             } else {
     282          210 :                 item->score()->addSpanner(spanner);
     283              :             }
     284          210 :         } else if (info->isEnd()) {
     285          210 :             spanner->setTrack2(l.track());
     286          210 :             spanner->setTick2(item->tick());
     287          210 :             spanner->setEndElement(item);
     288          210 :             if (pasteMode) {
     289            0 :                 for (EngravingObject* ee : spanner->linkList()) {
     290            0 :                     if (ee == spanner) {
     291            0 :                         continue;
     292              :                     }
     293            0 :                     Spanner* ls = toSpanner(ee);
     294            0 :                     ls->setTick2(spanner->tick2());
     295            0 :                     for (EngravingObject* eee : item->linkList()) {
     296            0 :                         ChordRest* cr = toChordRest(eee);
     297            0 :                         if (cr->score() == eee->score() && cr->staffIdx() == ls->staffIdx()) {
     298            0 :                             ls->setTrack2(cr->track());
     299            0 :                             if (ls->isSlur()) {
     300            0 :                                 ls->setEndElement(cr);
     301              :                             }
     302            0 :                             break;
     303              :                         }
     304            0 :                     }
     305            0 :                 }
     306              :             }
     307              :         } else {
     308            0 :             LOGD("ChordRest::readAddConnector(): Slur end is neither start nor end");
     309              :         }
     310              :     }
     311          420 :     break;
     312            0 :     default:
     313            0 :         break;
     314              :     }
     315          420 : }
     316              : 
     317          662 : void ConnectorInfoReader::readAddConnector(Measure* item, ConnectorInfoReader* info, bool pasteMode)
     318              : {
     319          662 :     const ElementType type = info->type();
     320          662 :     switch (type) {
     321          662 :     case ElementType::HAIRPIN:
     322              :     case ElementType::PEDAL:
     323              :     case ElementType::OTTAVA:
     324              :     case ElementType::TRILL:
     325              :     case ElementType::TEXTLINE:
     326              :     case ElementType::LET_RING:
     327              :     case ElementType::GRADUAL_TEMPO_CHANGE:
     328              :     case ElementType::VIBRATO:
     329              :     case ElementType::PALM_MUTE:
     330              :     case ElementType::WHAMMY_BAR:
     331              :     case ElementType::RASGUEADO:
     332              :     case ElementType::HARMONIC_MARK:
     333              :     case ElementType::PICK_SCRAPE:
     334              :     case ElementType::VOLTA:
     335              :     {
     336          662 :         Spanner* sp = toSpanner(info->connector());
     337          662 :         const Location& l = info->location();
     338          662 :         Fraction lTick    = l.frac();
     339          662 :         Fraction spTick   = pasteMode ? lTick : (item->tick() + lTick);
     340          662 :         if (info->isStart()) {
     341          331 :             sp->setTrack(l.track());
     342          331 :             sp->setTrack2(sp->track());
     343          331 :             sp->setTick(spTick);
     344              :             // Defer computing the end element: the end anchor (tick2/track2) is only known once the
     345              :             // matching end connector is processed (below). Computing it here would cache a wrong value
     346              :             // (tick2 == tick) that nothing recomputes until layout. See readAddConnector end branch.
     347          331 :             item->score()->addSpanner(sp, /*computeStartEnd=*/ false);
     348          331 :             sp->computeStartElement();
     349          331 :         } else if (info->isEnd()) {
     350          331 :             sp->setTrack2(l.track());
     351          331 :             sp->setTick2(spTick);
     352              :             // Now that the end anchor is set, compute the end element once with the final value.
     353          331 :             sp->computeEndElement();
     354              :         }
     355              :     }
     356          662 :     break;
     357            0 :     default:
     358            0 :         break;
     359              :     }
     360          662 : }
     361              : 
     362         1980 : void ConnectorInfoReader::readAddConnector(Note* item, ConnectorInfoReader* info, bool pasteMode)
     363              : {
     364         1980 :     const ElementType type = info->type();
     365         1980 :     const Location& l = info->location();
     366         1980 :     switch (type) {
     367         1980 :     case ElementType::TIE:
     368              :     case ElementType::TEXTLINE:
     369              :     case ElementType::GLISSANDO:
     370              :     {
     371         1980 :         Spanner* sp = toSpanner(info->connector());
     372         1980 :         if (info->isStart()) {
     373          990 :             sp->setTrack(l.track());
     374          990 :             sp->setTick(item->tick());
     375          990 :             if (sp->isTie()) {
     376          921 :                 Note* n = item;
     377          921 :                 while (n->tieFor()) {
     378            0 :                     n = n->tieFor()->endNote();
     379              :                 }
     380          921 :                 Tie* tie = toTie(sp);
     381          921 :                 tie->setOwnershipParent(n);
     382          921 :                 tie->setStartNote(n);
     383          921 :                 n->setTieFor(tie);
     384              :             } else {
     385           69 :                 sp->setStartElement(item);
     386           69 :                 item->addSpannerFor(sp);
     387           69 :                 sp->setOwnershipParent(item);
     388              :             }
     389          990 :         } else if (info->isEnd()) {
     390          990 :             sp->setTrack2(l.track());
     391          990 :             sp->setTick2(item->tick());
     392          990 :             sp->setEndElement(item);
     393          990 :             if (sp->isTie()) {
     394          921 :                 item->setTieBack(toTie(sp));
     395              :             } else {
     396           69 :                 bool isNoteAnchoredTextLine = sp->isNoteLine() && toNoteLine(sp)->enforceMinLength();
     397           69 :                 if ((sp->isGlissando() || isNoteAnchoredTextLine) && item->ownershipParent() && item->ownershipParent()->isChord()) {
     398           69 :                     toChord(item->ownershipParent())->setEndsNoteAnchoredLine(true);
     399              :                 }
     400           69 :                 item->addSpannerBack(sp);
     401              :             }
     402              : 
     403              :             // As spanners get added after being fully read, they
     404              :             // do not get cloned with the note when pasting to
     405              :             // linked staves. So add this spanner explicitly.
     406          990 :             if (pasteMode) {
     407            0 :                 item->score()->undoAddElement(sp);
     408              :             }
     409              :         }
     410              :     }
     411              :     default:
     412         1980 :         break;
     413              :     }
     414         1980 : }
     415              : 
     416            0 : void ConnectorInfoReader::readAddConnector(Score* item, ConnectorInfoReader* info, bool pasteMode)
     417              : {
     418            0 :     if (!pasteMode) {
     419              :         // How did we get there?
     420            0 :         LOGD("Score::readAddConnector is called not in paste mode.");
     421            0 :         return;
     422              :     }
     423            0 :     const ElementType type = info->type();
     424            0 :     switch (type) {
     425            0 :     case ElementType::HAIRPIN:
     426              :     case ElementType::PEDAL:
     427              :     case ElementType::OTTAVA:
     428              :     case ElementType::TRILL:
     429              :     case ElementType::TEXTLINE:
     430              :     case ElementType::VOLTA:
     431              :     case ElementType::PALM_MUTE:
     432              :     case ElementType::WHAMMY_BAR:
     433              :     case ElementType::RASGUEADO:
     434              :     case ElementType::HARMONIC_MARK:
     435              :     case ElementType::PICK_SCRAPE:
     436              :     case ElementType::LET_RING:
     437              :     case ElementType::GRADUAL_TEMPO_CHANGE:
     438              :     case ElementType::VIBRATO:
     439              :     {
     440            0 :         Spanner* sp = toSpanner(info->connector());
     441            0 :         const Location& l = info->location();
     442            0 :         if (info->isStart()) {
     443            0 :             sp->setTrack(l.track());
     444            0 :             sp->setTrack2(l.track());
     445            0 :             sp->setTick(l.frac());
     446            0 :         } else if (info->isEnd()) {
     447            0 :             sp->setTick2(l.frac());
     448            0 :             item->undoAddElement(sp);
     449            0 :             if (sp->isOttava()) {
     450            0 :                 sp->staff()->updateOttava();
     451              :             }
     452              :         }
     453              :     }
     454            0 :     break;
     455            0 :     default:
     456            0 :         break;
     457              :     }
     458              : }
        

Generated by: LCOV version 2.0-1