LCOV - code coverage report
Current view: top level - src/engraving/dom - harppedaldiagram.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 62.8 % 196 123
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 "harppedaldiagram.h"
      24              : 
      25              : #include "translation.h"
      26              : #include "types/typesconv.h"
      27              : 
      28              : #include "../editing/editharppedaldiagram.h"
      29              : 
      30              : #include "part.h"
      31              : #include "score.h"
      32              : #include "segment.h"
      33              : 
      34              : #include "log.h"
      35              : 
      36              : using namespace mu;
      37              : using namespace mu::engraving;
      38              : 
      39              : // Position of separator character in diagram
      40              : static constexpr size_t SEPARATOR_IDX = 3;
      41              : 
      42           93 : static const String harpStringTypeToString(HarpStringType type)
      43              : {
      44           93 :     switch (type) {
      45           14 :     case HarpStringType::A:
      46           14 :         return String(u"A");
      47           12 :     case HarpStringType::B:
      48           12 :         return String(u"B");
      49           14 :     case HarpStringType::C:
      50           14 :         return String(u"C");
      51           15 :     case HarpStringType::D:
      52           15 :         return String(u"D");
      53           11 :     case HarpStringType::E:
      54           11 :         return String(u"E");
      55           14 :     case HarpStringType::F:
      56           14 :         return String(u"F");
      57           13 :     case HarpStringType::G:
      58           13 :         return String(u"G");
      59              :     }
      60            0 :     return String();
      61              : }
      62              : 
      63              : // STYLE
      64              : static const ElementStyle harpPedalDiagramStyle {
      65              :     { Sid::harpPedalDiagramPlacement, Pid::PLACEMENT },
      66              :     { Sid::harpPedalDiagramMinDistance, Pid::MIN_DISTANCE },
      67              : };
      68              : 
      69              : static const ElementStyle harpPedalTextDiagramStyle {
      70              :     { Sid::harpPedalTextDiagramPlacement, Pid::PLACEMENT },
      71              :     { Sid::harpPedalTextDiagramMinDistance, Pid::MIN_DISTANCE },
      72              : };
      73              : 
      74              : // HarpPedalDiagram
      75          157 : void HarpPedalDiagram::setPlayableTpcs()
      76              : {
      77          157 :     std::set<int> playableTpcs;
      78              :     static const std::map<HarpStringType, int> string2step = {
      79              :         { HarpStringType::C, 0 },
      80              :         { HarpStringType::D, 1 },
      81              :         { HarpStringType::E, 2 },
      82              :         { HarpStringType::F, 3 },
      83              :         { HarpStringType::G, 4 },
      84              :         { HarpStringType::A, 5 },
      85              :         { HarpStringType::B, 6 }
      86          163 :     };
      87              :     static const std::map<PedalPosition, AccidentalVal> position2accidentalVal = {
      88              :         { PedalPosition::FLAT, AccidentalVal::FLAT },
      89              :         { PedalPosition::NATURAL, AccidentalVal::NATURAL },
      90              :         { PedalPosition::SHARP, AccidentalVal::SHARP }
      91          163 :     };
      92              : 
      93         2512 :     for (size_t i = 0; i < m_pedalState.size(); i++) {
      94         1099 :         int stringStep = string2step.at(HarpStringType(i));
      95         1099 :         AccidentalVal accidentalVal = position2accidentalVal.at(m_pedalState[i]);
      96         1099 :         int resTpc = step2tpc(stringStep, accidentalVal);
      97         1099 :         playableTpcs.insert(resTpc);
      98              :     }
      99              : 
     100          157 :     m_playableTpcs = playableTpcs;
     101          157 : }
     102              : 
     103           22 : HarpPedalDiagram::HarpPedalDiagram(DummyParentOr<Segment> parent)
     104           22 :     : TextBase(ElementType::HARP_DIAGRAM, parent, TextStyleType::HARP_PEDAL_DIAGRAM, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
     105              : {
     106           22 :     initElementStyle(&harpPedalDiagramStyle);
     107              :     m_pedalState
     108           22 :         = std::array<PedalPosition, HARP_STRING_NO> { PedalPosition::NATURAL, PedalPosition::NATURAL, PedalPosition::NATURAL,
     109              :                                                       PedalPosition::NATURAL,
     110              :                                                       PedalPosition::NATURAL, PedalPosition::NATURAL, PedalPosition::NATURAL };
     111           22 :     setPlayableTpcs();
     112           22 : }
     113              : 
     114            0 : HarpPedalDiagram::HarpPedalDiagram(const HarpPedalDiagram& h)
     115            0 :     : TextBase(h)
     116              : {
     117            0 :     m_pedalState = h.m_pedalState;
     118            0 :     m_isDiagram = h.m_isDiagram;
     119            0 :     setPlayableTpcs();
     120            0 : }
     121              : 
     122            6 : void HarpPedalDiagram::setPedalState(std::array<PedalPosition, HARP_STRING_NO> state)
     123              : {
     124            6 :     m_pedalState = state;
     125            6 :     setPlayableTpcs();
     126            6 : }
     127              : 
     128           18 : void HarpPedalDiagram::setIsDiagram(bool diagram)
     129              : {
     130           18 :     m_isDiagram = diagram;
     131           18 :     const ElementStyle& styleMap = m_isDiagram ? harpPedalDiagramStyle : harpPedalTextDiagramStyle;
     132              : 
     133           18 :     if (m_isDiagram) {
     134            3 :         setTextStyleType(TextStyleType::HARP_PEDAL_DIAGRAM);
     135              :     } else {
     136           15 :         setTextStyleType(TextStyleType::HARP_PEDAL_TEXT_DIAGRAM);
     137              :     }
     138              : 
     139           18 :     initElementStyle(&styleMap);
     140           18 : }
     141              : 
     142          116 : void HarpPedalDiagram::setPedal(HarpStringType harpString, PedalPosition pedal)
     143              : {
     144          116 :     m_pedalState.at(harpString) = pedal;
     145          116 :     setPlayableTpcs();
     146          116 : }
     147              : 
     148           32 : String HarpPedalDiagram::createDiagramText()
     149              : {
     150           32 :     String diagram;
     151           32 :     if (m_isDiagram) {
     152          144 :         for (size_t idx = 0; idx < m_pedalState.size(); idx++) {
     153           63 :             if (idx == SEPARATOR_IDX) {
     154              :                 // insert separator
     155            9 :                 diagram.append(u"<sym>harpPedalDivider</sym>");
     156              :             }
     157           63 :             switch (m_pedalState[idx]) {
     158           12 :             case PedalPosition::FLAT:
     159           12 :                 diagram.append(u"<sym>harpPedalRaised</sym>");
     160           12 :                 break;
     161           36 :             case PedalPosition::NATURAL:
     162           36 :                 diagram.append(u"<sym>harpPedalCentered</sym>");
     163           36 :                 break;
     164           15 :             case PedalPosition::SHARP:
     165           15 :                 diagram.append(u"<sym>harpPedalLowered</sym>");
     166           15 :                 break;
     167            0 :             case PedalPosition::UNSET:
     168            0 :                 break;
     169              :             }
     170              :         }
     171              :     } else {
     172              :         // find difference between pedal state of previous diagram and this one
     173              :         // if no previous, the pedals are "unset" meaning the whole diagram will be displayed
     174              : 
     175           23 :         constexpr std::array<PedalPosition, HARP_STRING_NO> initState
     176              :             = { PedalPosition::UNSET, PedalPosition::UNSET, PedalPosition::UNSET, PedalPosition::UNSET, PedalPosition::UNSET,
     177              :                 PedalPosition::UNSET, PedalPosition::UNSET };
     178              :         std::array<PedalPosition, HARP_STRING_NO> prevState;
     179           23 :         HarpPedalDiagram* prevDiagram = nullptr;
     180           23 :         if (originPart() && segment()) {
     181           23 :             prevDiagram = originPart()->prevHarpDiagram(segment()->tick());
     182              :         }
     183              : 
     184           23 :         if (prevDiagram != nullptr) {
     185              :             // If states are the same work backwards until we find the first difference then calculate from there
     186              :             // This ensures the diagram is identical to the previous one
     187           14 :             prevState = prevDiagram->getPedalState();
     188           16 :             while (prevState == m_pedalState) {
     189              :                 // if states are the same but previous is a diagram write whole config out
     190            2 :                 if (prevDiagram->isDiagram()) {
     191            0 :                     prevState = initState;
     192            0 :                     break;
     193              :                 }
     194              :                 // Check if there's a diagram before.  If not, start of score and use init state
     195            2 :                 prevDiagram = originPart()->prevHarpDiagram(prevDiagram->segment()->tick());
     196            2 :                 if (prevDiagram != nullptr) {
     197            2 :                     prevState = prevDiagram->getPedalState();
     198              :                 } else {
     199              :                     // set to init state and break loop
     200            0 :                     prevState = initState;
     201            0 :                     break;
     202              :                 }
     203              :             }
     204              :         } else {
     205            9 :             prevState = initState;
     206              :         }
     207              : 
     208           23 :         String topLine, bottomLine;
     209              : 
     210          368 :         for (size_t idx = 0; idx < m_pedalState.size(); idx++) {
     211          161 :             if (m_pedalState[idx] != prevState[idx]) {
     212           93 :                 String strName = harpStringTypeToString(HarpStringType(idx));
     213           93 :                 switch (m_pedalState[idx]) {
     214           21 :                 case PedalPosition::FLAT:
     215           21 :                     strName.append(u"<sym>csymAccidentalFlat</sym> ");
     216           21 :                     break;
     217           55 :                 case PedalPosition::NATURAL:
     218           55 :                     strName.append(u"<sym>csymAccidentalNatural</sym> ");
     219           55 :                     break;
     220           17 :                 case PedalPosition::SHARP:
     221           17 :                     strName.append(u"<sym>csymAccidentalSharp</sym> ");
     222           17 :                     break;
     223            0 :                 case PedalPosition::UNSET:
     224            0 :                     break;
     225              :                 }
     226           93 :                 if (idx < SEPARATOR_IDX) {
     227           41 :                     bottomLine.append(strName);
     228              :                 } else {
     229           52 :                     topLine.append(strName);
     230              :                 }
     231           93 :             }
     232              :         }
     233              : 
     234           23 :         if (topLine.size() == 0) {
     235            0 :             diagram.append(bottomLine);
     236              :         } else {
     237           23 :             diagram.append(topLine + u"\n" + bottomLine);
     238              :         }
     239           23 :     }
     240              : 
     241           32 :     return diagram;
     242            0 : }
     243              : 
     244           32 : void HarpPedalDiagram::updateDiagramText()
     245              : {
     246           32 :     undoChangeProperty(Pid::TEXT, createDiagramText(), PropertyFlags::STYLED);
     247           32 : }
     248              : 
     249            0 : void HarpPedalDiagram::undoChangePedalState(std::array<PedalPosition, HARP_STRING_NO> _pedalState)
     250              : {
     251            0 :     const std::list<EngravingObject*> links = linkList();
     252            0 :     for (EngravingObject* obj : links) {
     253            0 :         if (!obj || !obj->isHarpPedalDiagram()) {
     254            0 :             continue;
     255              :         }
     256              : 
     257            0 :         HarpPedalDiagram* item = toHarpPedalDiagram(obj);
     258            0 :         Score* linkedScore = item->score();
     259            0 :         if (!linkedScore) {
     260            0 :             continue;
     261              :         }
     262              : 
     263            0 :         linkedScore->undo(new ChangeHarpPedalState(item, _pedalState));
     264              :     }
     265            0 : }
     266              : 
     267            3 : bool HarpPedalDiagram::isTpcPlayable(int tpc)
     268              : {
     269            3 :     return m_playableTpcs.find(tpc) != m_playableTpcs.cend();
     270              : }
     271              : 
     272          564 : PropertyValue HarpPedalDiagram::getProperty(Pid propertyId) const
     273              : {
     274          564 :     switch (propertyId) {
     275           12 :     case Pid::HARP_IS_DIAGRAM:
     276           12 :         return m_isDiagram;
     277          552 :     default:
     278          552 :         return TextBase::getProperty(propertyId);
     279              :     }
     280              : }
     281              : 
     282         1000 : bool HarpPedalDiagram::setProperty(Pid propertyId, const PropertyValue& v)
     283              : {
     284         1000 :     switch (propertyId) {
     285            0 :     case Pid::HARP_IS_DIAGRAM:
     286            0 :         setIsDiagram(v.toBool());
     287            0 :         break;
     288         1000 :     default:
     289         1000 :         if (!TextBase::setProperty(propertyId, v)) {
     290            0 :             return false;
     291              :         }
     292              :     }
     293         1000 :     return true;
     294              : }
     295              : 
     296          235 : PropertyValue HarpPedalDiagram::propertyDefault(Pid id) const
     297              : {
     298          235 :     switch (id) {
     299           12 :     case Pid::TEXT_STYLE:
     300           12 :         return TextStyleType::HARP_PEDAL_DIAGRAM;
     301           12 :     case Pid::HARP_IS_DIAGRAM:
     302           12 :         return true;
     303          211 :     default:
     304          211 :         return TextBase::propertyDefault(id);
     305              :     }
     306              : }
     307              : 
     308            0 : String HarpPedalDiagram::accessibleInfo() const
     309              : {
     310            0 :     String rez = score() ? score()->getTextStyleUserName(textStyleType()).translated() : TConv::translatedUserName(textStyleType());
     311            0 :     String s;
     312              : 
     313            0 :     for (size_t idx = 0; idx < m_pedalState.size(); idx++) {
     314            0 :         s.append(harpStringTypeToString(HarpStringType(idx)));
     315            0 :         switch (m_pedalState[idx]) {
     316            0 :         case PedalPosition::FLAT:
     317            0 :             s.append(u"♭ ");
     318            0 :             break;
     319            0 :         case PedalPosition::NATURAL:
     320            0 :             s.append(u"♮ ");
     321            0 :             break;
     322            0 :         case PedalPosition::SHARP:
     323            0 :             s.append(u"♯ ");
     324            0 :             break;
     325            0 :         case PedalPosition::UNSET:
     326            0 :             s.append(u" unset ");
     327            0 :             break;
     328              :         }
     329              :     }
     330              : 
     331              :     // trim trailing space
     332            0 :     s.truncate(s.size() - 1);
     333              : 
     334            0 :     return String(u"%1: %2").arg(rez, s);
     335            0 : }
     336              : 
     337            0 : String HarpPedalDiagram::screenReaderInfo() const
     338              : {
     339            0 :     String rez = score() ? score()->getTextStyleUserName(textStyleType()).translated() : TConv::translatedUserName(textStyleType());
     340            0 :     String s;
     341              : 
     342            0 :     for (size_t idx = 0; idx < m_pedalState.size(); idx++) {
     343            0 :         s.append(harpStringTypeToString(HarpStringType(idx)) + u" ");
     344            0 :         switch (m_pedalState.at(idx)) {
     345            0 :         case PedalPosition::FLAT:
     346            0 :             s.append(muse::mtrc("engraving", TConv::userName(AccidentalVal::FLAT, true)));
     347            0 :             break;
     348            0 :         case PedalPosition::NATURAL:
     349            0 :             s.append(muse::mtrc("engraving", TConv::userName(AccidentalVal::NATURAL, true)));
     350            0 :             break;
     351            0 :         case PedalPosition::SHARP:
     352            0 :             s.append(muse::mtrc("engraving", TConv::userName(AccidentalVal::SHARP, true)));
     353            0 :             break;
     354            0 :         case PedalPosition::UNSET:
     355            0 :             s.append(u" unset ");
     356            0 :             break;
     357              :         }
     358              :     }
     359              : 
     360            0 :     return String(u"%1: %2").arg(rez, s);
     361            0 : }
        

Generated by: LCOV version 2.0-1