LCOV - code coverage report
Current view: top level - src/engraving/dom - stringtunings.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 32.4 % 173 56
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) 2022 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 "stringtunings.h"
      24              : 
      25              : #include "types/typesconv.h"
      26              : #include "utils.h"
      27              : 
      28              : #include "part.h"
      29              : #include "score.h"
      30              : #include "segment.h"
      31              : #include "staff.h"
      32              : #include "../editing/editpart.h"
      33              : 
      34              : #include "containers.h"
      35              : #include "translation.h"
      36              : 
      37              : using namespace mu;
      38              : using namespace mu::engraving;
      39              : 
      40              : // STYLE
      41              : static const ElementStyle STRING_TUNINGS_STYLE {
      42              :     { Sid::staffTextPlacement, Pid::PLACEMENT },
      43              :     { Sid::staffTextMinDistance, Pid::MIN_DISTANCE },
      44              : };
      45              : 
      46           19 : StringTunings::StringTunings(DummyParentOr<Segment> parent, TextStyleType textStyleType)
      47           19 :     : StaffTextBase(ElementType::STRING_TUNINGS, parent, textStyleType, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      48              : {
      49           19 :     initElementStyle(&STRING_TUNINGS_STYLE);
      50           19 : }
      51              : 
      52            0 : StringTunings::StringTunings(const StringTunings& s)
      53            0 :     : StaffTextBase(s)
      54              : {
      55            0 :     m_preset = s.m_preset;
      56            0 :     m_visibleStrings = s.m_visibleStrings;
      57            0 :     m_stringData = s.m_stringData;
      58              : 
      59            0 :     m_noStringVisible = s.m_noStringVisible;
      60            0 :     m_stringsNumber = s.m_stringsNumber;
      61            0 : }
      62              : 
      63            0 : StringTunings* StringTunings::clone() const
      64              : {
      65            0 :     return new StringTunings(*this);
      66              : }
      67              : 
      68            0 : bool StringTunings::isEditable() const
      69              : {
      70            0 :     return false;
      71              : }
      72              : 
      73          266 : PropertyValue StringTunings::getProperty(Pid id) const
      74              : {
      75          266 :     if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
      76            0 :         Fraction tick = this->tick();
      77            0 :         if (staff()->isTabStaff(tick)) {
      78            0 :             return staff()->lines(tick);
      79              :         } else {
      80            0 :             for (const Staff* _staff : staff()->staffList()) {
      81            0 :                 if (_staff == staff()) {
      82            0 :                     continue;
      83              :                 }
      84              : 
      85            0 :                 if (_staff->score() == staff()->score() && _staff->isTabStaff(tick)) {
      86            0 :                     return _staff->lines(tick);
      87              :                 }
      88            0 :             }
      89              :         }
      90              : 
      91            0 :         if (m_stringsNumber.has_value()) {
      92            0 :             return m_stringsNumber.value();
      93              :         }
      94              : 
      95            0 :         return stringData()->strings();
      96          266 :     } else if (id == Pid::STRINGTUNINGS_PRESET) {
      97           19 :         return m_preset;
      98          247 :     } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
      99            0 :         std::vector<int> visibleStrings;
     100            0 :         for (string_idx_t string : m_visibleStrings) {
     101            0 :             visibleStrings.emplace_back(static_cast<int>(string));
     102              :         }
     103              : 
     104            0 :         return visibleStrings;
     105            0 :     }
     106              : 
     107          247 :     return StaffTextBase::getProperty(id);
     108              : }
     109              : 
     110          380 : PropertyValue StringTunings::propertyDefault(Pid id) const
     111              : {
     112          380 :     if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
     113            0 :         return stringData()->strings();
     114          380 :     } else if (id == Pid::STRINGTUNINGS_PRESET) {
     115           38 :         return String();
     116          361 :     } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
     117            0 :         return {};
     118              :     }
     119              : 
     120          361 :     return StaffTextBase::propertyDefault(id);
     121              : }
     122              : 
     123          361 : bool StringTunings::setProperty(Pid id, const PropertyValue& val)
     124              : {
     125          361 :     if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
     126            0 :         Fraction tick = this->tick();
     127            0 :         if (staff()->isTabStaff(tick)) {
     128            0 :             staff()->staffType(tick)->setLines(val.toInt());
     129              :         } else {
     130            0 :             for (Staff* _staff : staff()->staffList()) {
     131            0 :                 if (_staff == staff()) {
     132            0 :                     continue;
     133              :                 }
     134              : 
     135            0 :                 if (_staff->score() == staff()->score() && _staff->isTabStaff(tick)) {
     136            0 :                     _staff->staffType(tick)->setLines(val.toInt());
     137              :                 }
     138            0 :             }
     139              :         }
     140              : 
     141            0 :         m_stringsNumber = val.toInt();
     142          361 :     } else if (id == Pid::STRINGTUNINGS_PRESET) {
     143            0 :         m_preset = val.value<String>();
     144          361 :     } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
     145            0 :         m_visibleStrings.clear();
     146            0 :         std::vector<int> ignoredStrings = val.value<std::vector<int> >();
     147            0 :         for (int string : ignoredStrings) {
     148            0 :             m_visibleStrings.emplace_back(static_cast<string_idx_t>(string));
     149              :         }
     150            0 :     } else {
     151          361 :         return StaffTextBase::setProperty(id, val);
     152              :     }
     153              : 
     154            0 :     triggerLayout();
     155            0 :     return true;
     156              : }
     157              : 
     158            0 : String StringTunings::accessibleInfo() const
     159              : {
     160            0 :     const StringData* stringData = this->stringData();
     161            0 :     if (stringData->isNull()) {
     162            0 :         return String();
     163              :     }
     164              : 
     165            0 :     String elementName = score() ? score()->getTextStyleUserName(TextStyleType::STRING_TUNINGS).translated()
     166            0 :                          : TConv::translatedUserName(TextStyleType::STRING_TUNINGS);
     167            0 :     String info;
     168              : 
     169            0 :     const std::vector<instrString>& stringList = stringData->stringList();
     170            0 :     int numOfStrings = static_cast<int>(stringList.size());
     171            0 :     for (int i = 0; i < numOfStrings; ++i) {
     172            0 :         string_idx_t index = numOfStrings - i - 1;
     173            0 :         if (muse::contains(m_visibleStrings, index)) {
     174            0 :             const instrString str = stringList[index];
     175            0 :             String pitchStr = pitch2string(str.pitch, str.useFlat);
     176            0 :             if (pitchStr.empty()) {
     177            0 :                 LOGE() << "Invalid get pitch name for " << str.pitch;
     178            0 :                 continue;
     179              :             }
     180              : 
     181            0 :             info += muse::mtrc("engraving", "String %1").arg(String::number(i + 1)) + u", "
     182            0 :                     + muse::mtrc("engraving", "Value %1").arg(pitchStr) + u"; ";
     183            0 :         }
     184              :     }
     185              : 
     186            0 :     return String(u"%1: %2").arg(elementName, info);
     187            0 : }
     188              : 
     189            0 : String StringTunings::screenReaderInfo() const
     190              : {
     191            0 :     return accessibleInfo();
     192              : }
     193              : 
     194           60 : const StringData* StringTunings::stringData() const
     195              : {
     196           60 :     return &m_stringData;
     197              : }
     198              : 
     199           19 : void StringTunings::setStringData(const StringData& stringData)
     200              : {
     201           19 :     m_stringData.set(stringData);
     202           19 :     triggerLayout();
     203           19 : }
     204              : 
     205            0 : void StringTunings::undoStringData(const StringData& stringData)
     206              : {
     207            0 :     score()->undo(new ChangeStringData(this, stringData));
     208            0 :     triggerLayout();
     209            0 : }
     210              : 
     211            0 : const String& StringTunings::preset() const
     212              : {
     213            0 :     return m_preset;
     214              : }
     215              : 
     216            0 : void StringTunings::setPreset(const String& preset)
     217              : {
     218            0 :     m_preset = preset;
     219            0 : }
     220              : 
     221           19 : const std::vector<string_idx_t>& StringTunings::visibleStrings() const
     222              : {
     223           19 :     return m_visibleStrings;
     224              : }
     225              : 
     226            0 : void StringTunings::setVisibleStrings(const std::vector<string_idx_t>& visibleStrings)
     227              : {
     228            0 :     m_visibleStrings = visibleStrings;
     229            0 : }
     230              : 
     231           19 : void StringTunings::updateText()
     232              : {
     233           19 :     String updatedText = generateText();
     234           19 :     undoChangeProperty(Pid::TEXT, updatedText, PropertyFlags::STYLED);
     235              : 
     236           19 :     if (updatedText.empty()) {
     237           19 :         m_noStringVisible = true;
     238              :     } else {
     239            0 :         m_noStringVisible = false;
     240              :     }
     241           19 : }
     242              : 
     243           19 : bool StringTunings::noStringVisible() const
     244              : {
     245           19 :     return m_noStringVisible;
     246              : }
     247              : 
     248          456 : void StringTunings::triggerLayout() const
     249              : {
     250          456 :     if (!part()) {
     251          437 :         return StaffTextBase::triggerLayout();
     252              :     }
     253              : 
     254           19 :     Fraction startTick = tick();
     255              : 
     256           19 :     StringTunings* nextStringTuning = originPart()->nextStringTuning(startTick);
     257              : 
     258           19 :     Fraction endTick = nextStringTuning ? nextStringTuning->tick() : score()->endTick();
     259              : 
     260           19 :     score()->setLayout(startTick, endTick, staffIdx(), staffIdx(), this);
     261              : }
     262              : 
     263           19 : String StringTunings::generateText() const
     264              : {
     265           19 :     const StringData* stringData = this->stringData();
     266           19 :     if (!stringData || stringData->isNull()) {
     267            0 :         return u"";
     268              :     }
     269              : 
     270            0 :     auto guitarStringSymbol = [](int i) { return String(u"<sym>guitarString") + String::number(i) + u"</sym>"; };
     271              : 
     272           19 :     const std::vector<instrString>& stringList = stringData->stringList();
     273           19 :     std::vector<String> visibleStringList;
     274           19 :     int numOfStrings = static_cast<int>(stringList.size());
     275          127 :     for (int i = 0; i < numOfStrings; ++i) {
     276          108 :         string_idx_t index = numOfStrings - i - 1;
     277          108 :         if (muse::contains(m_visibleStrings, index)) {
     278            0 :             const instrString str = stringList[index];
     279            0 :             String pitchStr = pitch2string(str.pitch, str.useFlat);
     280            0 :             if (pitchStr.empty()) {
     281            0 :                 LOGE() << "Invalid get pitch name for " << str.pitch;
     282            0 :                 continue;
     283              :             }
     284              : 
     285            0 :             Char accidental;
     286            0 :             if (pitchStr.size() > 1) {
     287            0 :                 Char sym(pitchStr[1]);
     288            0 :                 if (!sym.isDigit()) {
     289            0 :                     accidental = sym;
     290              :                 }
     291              :             }
     292              : 
     293            0 :             visibleStringList.emplace_back(String(guitarStringSymbol(i + 1) + u" \u2013 "
     294            0 :                                                   + String(pitchStr[0]).toUpper() + accidental) + u"  ");
     295            0 :         }
     296              :     }
     297              : 
     298           19 :     if (visibleStringList.empty()) {
     299           19 :         return u"";
     300              :     }
     301              : 
     302            0 :     size_t columnCount = 0;
     303            0 :     size_t rowCount = 0;
     304              : 
     305            0 :     if (visibleStringList.size() < 4) {
     306            0 :         rowCount = visibleStringList.size();
     307            0 :         columnCount = 1;
     308              :     } else {
     309            0 :         rowCount = std::ceil(static_cast<double>(visibleStringList.size()) / 2);
     310            0 :         columnCount = 2;
     311              :     }
     312              : 
     313            0 :     String result;
     314            0 :     for (size_t i = 0; i < rowCount; ++i) {
     315            0 :         for (size_t j = 0; j < columnCount; ++j) {
     316            0 :             size_t index = i + j * rowCount;
     317            0 :             if (index < visibleStringList.size()) {
     318            0 :                 result += visibleStringList[index];
     319              :             }
     320            0 :             result += u"\t";
     321              :         }
     322            0 :         result += u"\n";
     323              :     }
     324              : 
     325            0 :     return result;
     326           19 : }
        

Generated by: LCOV version 2.0-1