LCOV - code coverage report
Current view: top level - src/engraving/dom - sharedpart.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 67.5 % 120 81
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              : #include "sharedpart.h"
      23              : #include "score.h"
      24              : #include "staff.h"
      25              : 
      26              : #include <sstream>
      27              : 
      28              : namespace mu::engraving {
      29            0 : std::string dump(const SharedTrackMap& map)
      30              : {
      31            0 :     std::stringstream ss;
      32            0 :     ss << "SharedTrackMap size: " << map.size() << " {";
      33            0 :     for (auto it = map.begin(); it != map.end(); ++it) {
      34            0 :         if (it != map.begin()) {
      35            0 :             ss << ", ";
      36              :         }
      37            0 :         ss << it->first << ": " << it->second;
      38              :     }
      39            0 :     ss << "}";
      40            0 :     return ss.str();
      41            0 : }
      42              : 
      43           11 : SharedPart::SharedPart(Score* score)
      44           44 :     : Part(score, ElementType::SHARED_PART)
      45              : {
      46           11 : }
      47              : 
      48           26 : void SharedPart::addOriginPart(Part* p)
      49              : {
      50           26 :     DO_ASSERT(p->type() == ElementType::PART && !muse::contains(m_originParts, p));
      51              : 
      52           26 :     const std::vector<Part*>& parts = score()->parts();
      53           26 :     if (muse::contains(parts, p)) {
      54           24 :         std::unordered_map<Part*, size_t> order;
      55           96 :         for (size_t i = 0; i < parts.size(); ++i) {
      56           72 :             order[parts[i]] = i;
      57              :         }
      58              : 
      59           24 :         auto it = std::lower_bound(m_originParts.begin(), m_originParts.end(), p, [&order](Part* part1, Part* part2) {
      60           12 :             return order[part1] < order[part2];
      61              :         });
      62              : 
      63           24 :         m_originParts.insert(it, p);
      64           24 :     } else {
      65              :         // Can happen while reading files as p has not been added to the score yet
      66            2 :         m_originParts.push_back(p);
      67              :     }
      68              : 
      69           26 :     p->setSharedPart(this);
      70              : 
      71           26 :     computeIsSameInstruments();
      72           26 : }
      73              : 
      74           12 : void SharedPart::removeOriginPart(Part* p)
      75              : {
      76           12 :     DO_ASSERT(muse::remove(m_originParts, p));
      77              : 
      78           12 :     p->setSharedPart(nullptr);
      79              : 
      80           12 :     computeIsSameInstruments();
      81           12 : }
      82              : 
      83         2832 : Part* SharedPart::mappedOriginPart(track_idx_t sharedTrack, const Fraction& tick) const
      84              : {
      85         2832 :     const SharedTrackMap& trackMap = trackMapAtTick(tick.negative() ? Fraction(0, 1) : tick);
      86              : 
      87         2832 :     track_idx_t originTrack = muse::key(trackMap, sharedTrack, muse::nidx);
      88              : 
      89         2832 :     const Staff* originStaff = originTrack != muse::nidx ? score()->staff(track2staff(originTrack)) : nullptr;
      90         2832 :     return originStaff ? originStaff->part() : nullptr;
      91              : }
      92              : 
      93         2595 : const Part* SharedPart::originPart(track_idx_t sharedTrack, const Fraction& tick) const
      94              : {
      95         2595 :     if (sharedTrack != muse::nidx) {
      96         2595 :         if (const Part* p = mappedOriginPart(sharedTrack, tick)) {
      97         1710 :             return p;
      98              :         }
      99              :     }
     100              : 
     101          885 :     return m_originParts.empty() ? this : m_originParts.front();
     102              : }
     103              : 
     104          237 : Part* SharedPart::originPartForStaff(staff_idx_t sharedStaffIdx, const Fraction& tick) const
     105              : {
     106          237 :     return mappedOriginPart(staff2track(sharedStaffIdx, 0), tick);
     107              : }
     108              : 
     109         3688 : const SharedTrackMap& SharedPart::trackMapAtTick(const Fraction& tick) const
     110              : {
     111              :     static constexpr Fraction TICK_ZERO = Fraction(0, 1);
     112              : 
     113         3688 :     IF_ASSERT_FAILED(m_trackMapsByTick.size() > 0) {
     114            0 :         const_cast<SharedPart*>(this)->m_trackMapsByTick[TICK_ZERO] = SharedTrackMap();
     115            0 :         return m_trackMapsByTick.at(TICK_ZERO);
     116              :     }
     117              : 
     118         3688 :     DO_ASSERT(muse::contains(m_trackMapsByTick, TICK_ZERO));
     119              : 
     120         3688 :     IF_ASSERT_FAILED(tick.positive()) {
     121            0 :         return m_trackMapsByTick.at(TICK_ZERO);
     122              :     }
     123              : 
     124         3688 :     auto upperBound = m_trackMapsByTick.upper_bound(tick);
     125         3688 :     DO_ASSERT(upperBound != m_trackMapsByTick.begin());
     126              : 
     127         3688 :     --upperBound;
     128              : 
     129         3688 :     return upperBound->second;
     130              : }
     131              : 
     132          351 : void SharedPart::setTrackMapAtTick(const SharedTrackMap& map, const Fraction& tick)
     133              : {
     134          351 :     m_trackMapsByTick[tick] = map;
     135          351 : }
     136              : 
     137          351 : void mu::engraving::SharedPart::removeMapsBetweenTicks(const Fraction& startTick, const Fraction& endTick)
     138              : {
     139          351 :     auto upperBound = m_trackMapsByTick.lower_bound(startTick);
     140          351 :     if (upperBound == m_trackMapsByTick.end()) {
     141           19 :         return;
     142              :     }
     143              : 
     144          668 :     for (auto iter = upperBound; iter != m_trackMapsByTick.end();) {
     145          452 :         if (iter->first >= endTick) {
     146          116 :             break;
     147              :         }
     148          336 :         iter = m_trackMapsByTick.erase(iter);
     149              :     }
     150              : }
     151              : 
     152          161 : bool SharedPart::isSameInstrumentsAtTick(const Fraction& tick)
     153              : {
     154          161 :     if (m_originParts.empty()) {
     155            6 :         return false;
     156              :     }
     157              : 
     158          155 :     const Instrument* instr = m_originParts.front()->instrument(tick);
     159          437 :     for (Part* p : m_originParts) {
     160          282 :         if (p->instrument(tick)->id() != instr->id()) {
     161            0 :             return false;
     162              :         }
     163              :     }
     164              : 
     165          155 :     return true;
     166              : }
     167              : 
     168           38 : void SharedPart::computeIsSameInstruments()
     169              : {
     170           38 :     m_isSameInstruments = isSameInstrumentsAtTick(Fraction(0, 1));
     171           38 : }
     172              : 
     173            0 : mu::engraving::String mu::engraving::SharedPart::partName() const
     174              : {
     175            0 :     const Instrument* i = instrument();
     176            0 :     String fullName = i->longName();
     177              : 
     178            0 :     const String& transp = i->transposition();
     179            0 :     if (!transp.empty()) {
     180              :         //: For instrument transposition, e.g. Horn in F
     181            0 :         fullName += u" " + muse::mtrc("engraving", "in") + u" " + transp;
     182              :     }
     183              : 
     184            0 :     int firstNumber = 10000;
     185            0 :     int lastNumber = 0;
     186              : 
     187            0 :     for (const Part* originPart : originParts()) {
     188            0 :         int number = originPart->instrument()->number();
     189            0 :         firstNumber = std::min(firstNumber, number);
     190            0 :         lastNumber = std::max(lastNumber, number);
     191              :     }
     192              : 
     193            0 :     if (firstNumber == lastNumber) {
     194            0 :         return fullName;
     195              :     }
     196              : 
     197            0 :     fullName += u" " + String::number(firstNumber) + u"-" + String::number(lastNumber);
     198              : 
     199            0 :     return fullName;
     200            0 : }
     201              : 
     202            2 : PropertyValue SharedPart::getProperty(Pid pid) const
     203              : {
     204            2 :     switch (pid) {
     205            1 :     case Pid::SHARED_PART_ENABLED:
     206            1 :         return m_enabled;
     207            1 :     default:
     208            1 :         return Part::getProperty(pid);
     209              :     }
     210              : }
     211              : 
     212            0 : PropertyValue SharedPart::propertyDefault(Pid pid) const
     213              : {
     214            0 :     switch (pid) {
     215            0 :     case Pid::SHARED_PART_ENABLED:
     216            0 :         return true;
     217            0 :     default:
     218            0 :         return Part::propertyDefault(pid);
     219              :     }
     220              : }
     221              : 
     222            1 : bool SharedPart::setProperty(Pid pid, const PropertyValue& v)
     223              : {
     224            1 :     switch (pid) {
     225            1 :     case Pid::SHARED_PART_ENABLED:
     226            1 :         m_enabled = v.toBool();
     227            1 :         break;
     228            0 :     default:
     229            0 :         return Part::setProperty(pid, v);
     230              :     }
     231              : 
     232            1 :     score()->setLayoutAll();
     233            1 :     return true;
     234              : }
     235              : 
     236        31996 : bool SharedPart::enabled() const
     237              : {
     238        31996 :     return m_enabled && style().styleB(Sid::enableStaveSharing);
     239              : }
     240              : 
     241        36002 : bool SharedPart::show() const
     242              : {
     243        36002 :     return m_show && m_enabled && style().styleB(Sid::enableStaveSharing);
     244              : }
     245              : }
        

Generated by: LCOV version 2.0-1