LCOV - code coverage report
Current view: top level - src/engraving/dom - fret.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 52.3 % 774 405
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 "fret.h"
      24              : 
      25              : #include "translation.h"
      26              : 
      27              : #include "io/file.h"
      28              : 
      29              : #include "../editing/addremoveelement.h"
      30              : #include "../editing/editfretboarddiagram.h"
      31              : 
      32              : #include "anchors.h"
      33              : #include "chord.h"
      34              : #include "factory.h"
      35              : #include "harmony.h"
      36              : #include "measure.h"
      37              : #include "note.h"
      38              : #include "pitchspelling.h"
      39              : #include "score.h"
      40              : #include "segment.h"
      41              : #include "staff.h"
      42              : #include "stringdata.h"
      43              : #include "system.h"
      44              : 
      45              : #include "rw/harmonytodiagramreader.h"
      46              : #include "rw/read460/tread.h"
      47              : 
      48              : #include "log.h"
      49              : 
      50              : using namespace mu;
      51              : using namespace muse::draw;
      52              : using namespace mu::engraving;
      53              : 
      54              : namespace mu::engraving {
      55              : //    parent() is Segment or Box
      56              : //
      57              : 
      58              : //---------------------------------------------------------
      59              : //   fretStyle
      60              : //---------------------------------------------------------
      61              : 
      62              : static const ElementStyle fretStyle {
      63              :     { Sid::fretNumPos,                         Pid::FRET_NUM_POS },
      64              :     { Sid::fretMag,                            Pid::MAG },
      65              :     { Sid::fretPlacement,                      Pid::PLACEMENT },
      66              :     { Sid::fretStrings,                        Pid::FRET_STRINGS },
      67              :     { Sid::fretFrets,                          Pid::FRET_FRETS },
      68              :     { Sid::fretNut,                            Pid::FRET_NUT },
      69              :     { Sid::fretMinDistance,                    Pid::MIN_DISTANCE },
      70              :     { Sid::fretOrientation,                    Pid::ORIENTATION },
      71              :     { Sid::fretShowFingerings,                 Pid::FRET_SHOW_FINGERINGS },
      72              : };
      73              : 
      74              : //---------------------------------------------------------
      75              : //   FretDiagram
      76              : //---------------------------------------------------------
      77              : 
      78              : struct HarmonyMapKey
      79              : {
      80              :     HarmonyMapKey() {}
      81         6353 :     HarmonyMapKey(int k, int r, int b)
      82         6353 :         : keys(k), rootTpc(r), bassTpc(b) {}
      83              : 
      84              :     int keys = 0;
      85              :     int rootTpc = Tpc::TPC_INVALID;
      86              :     int bassTpc = Tpc::TPC_INVALID;
      87              : 
      88        85780 :     bool operator<(const HarmonyMapKey& other) const
      89              :     {
      90        85780 :         return std::tie(keys, rootTpc, bassTpc) < std::tie(other.keys, other.rootTpc, other.bassTpc);
      91              :     }
      92              : 
      93              :     bool operator==(const HarmonyMapKey& other) const
      94              :     {
      95              :         return std::tie(keys, rootTpc, bassTpc) == std::tie(other.keys, other.rootTpc, other.bassTpc);
      96              :     }
      97              : };
      98              : 
      99              : static std::map<HarmonyMapKey /*key*/, std::vector<DiagramInfo> > s_harmonyToDiagramMap;
     100              : static std::unordered_map<String /*pattern*/, std::vector<String /*harmonyName*/> > s_diagramPatternToHarmoniesMap;
     101              : 
     102              : static const muse::io::path_t HARMONY_TO_DIAGRAM_FILE_PATH(":/data/harmony_to_diagram.xml");
     103              : 
     104         9865 : static int canonicalEnharmonicTpc(int tpc)
     105              : {
     106              :     // Normalize to one canonical TPC per pitch class
     107         9865 :     int mod = (tpc - Tpc::TPC_G_B) % TPC_DELTA_ENHARMONIC;
     108         9865 :     return Tpc::TPC_G_B + (mod < 0 ? mod + TPC_DELTA_ENHARMONIC : mod);
     109              : }
     110              : 
     111         6353 : static HarmonyMapKey createHarmonyMapKey(const String& harmony, const NoteSpellingType& spellingType, const ChordList* cl)
     112              : {
     113         6353 :     String s = harmony;
     114              :     NoteCaseType noteCase;
     115              :     size_t idx;
     116         6353 :     int rootTpc = convertNote(s, spellingType, noteCase, idx);
     117              : 
     118         6353 :     int bassTpc = Tpc::TPC_INVALID;
     119         6353 :     size_t slash = s.lastIndexOf(u'/');
     120         6353 :     if (slash != muse::nidx) {
     121         3534 :         String bs = s.mid(slash + 1).simplified();
     122         3534 :         s = s.mid(idx, slash - idx).simplified();
     123         3534 :         size_t idx2 = 0;
     124         3534 :         bassTpc = convertNote(bs, spellingType, noteCase, idx2);
     125              : 
     126         3534 :         if (!tpcIsValid(bassTpc)) {
     127              :             // if what follows after slash is not (just) a TPC
     128              :             // then reassemble chord and try to parse with the slash
     129           20 :             s = s + u"/" + bs;
     130              :         }
     131         3534 :     } else {
     132         2819 :         s = s.mid(idx);
     133              :     }
     134              : 
     135         6353 :     ParsedChord chord;
     136         6353 :     if (!chord.parse(s, cl)) {
     137            0 :         LOGE() << "Error parse " << harmony;
     138              :     }
     139              : 
     140         6353 :     int keys = chord.keys();
     141              : 
     142        12706 :     return HarmonyMapKey(keys, rootTpc, bassTpc);
     143         6353 : }
     144              : 
     145           69 : static DiagramInfo resolveDiagram(const std::vector<DiagramInfo>& diagrams)
     146              : {
     147           47 :     auto isSimpler = [](const DiagramInfo& a, const DiagramInfo& b) {
     148           47 :         const String& harmonyNameA = a.harmonyName;
     149           47 :         const String& harmonyNameB = b.harmonyName;
     150              : 
     151           47 :         if (harmonyNameA.size() != harmonyNameB.size()) {
     152           35 :             return harmonyNameA.size() < harmonyNameB.size();
     153              :         }
     154              : 
     155           12 :         return harmonyNameA < harmonyNameB;
     156              :     };
     157              : 
     158           69 :     DiagramInfo result = diagrams.front();
     159          116 :     for (size_t i = 1; i < diagrams.size(); ++i) {
     160           47 :         if (!isSimpler(result, diagrams[i])) {
     161           26 :             result = diagrams[i];
     162              :         }
     163              :     }
     164              : 
     165           69 :     return result;
     166            0 : }
     167              : 
     168          130 : FretDiagram::FretDiagram(DummyParentOr<Segment> parent)
     169          260 :     : EngravingItem(ElementType::FRET_DIAGRAM, parent, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
     170              : {
     171          130 :     initDefaultValues();
     172              : 
     173          130 :     initElementStyle(&fretStyle);
     174          130 : }
     175              : 
     176           22 : FretDiagram::FretDiagram(const FretDiagram& f)
     177           44 :     : EngravingItem(f)
     178              : {
     179           22 :     m_strings    = f.m_strings;
     180           22 :     m_frets      = f.m_frets;
     181           22 :     m_fretOffset = f.m_fretOffset;
     182           22 :     m_maxFrets   = f.m_maxFrets;
     183           22 :     m_userMag    = f.m_userMag;
     184           22 :     m_dots       = f.m_dots;
     185           22 :     m_markers    = f.m_markers;
     186           22 :     m_barres     = f.m_barres;
     187           22 :     m_showNut    = f.m_showNut;
     188           22 :     m_orientation= f.m_orientation;
     189           22 :     m_showFingering = f.m_showFingering;
     190           22 :     m_fingering = f.m_fingering;
     191              : 
     192           22 :     if (f.m_harmony) {
     193           22 :         Harmony* h = new Harmony(*f.m_harmony);
     194           22 :         add(h);
     195              :     }
     196           22 : }
     197              : 
     198          130 : void FretDiagram::initDefaultValues()
     199              : {
     200          130 :     m_strings = 6;
     201          130 :     m_frets = 4;
     202          130 :     m_fretOffset = 0;
     203          130 :     m_maxFrets = 24;
     204          130 :     m_showNut = true;
     205          130 :     m_orientation = Orientation::VERTICAL;
     206              : 
     207          130 :     m_userMag = 1.0;
     208              : 
     209          130 :     m_showFingering = false;
     210          130 :     m_fingering = std::vector<int>(m_strings, 0);
     211          130 : }
     212              : 
     213          300 : FretDiagram::~FretDiagram()
     214              : {
     215          150 :     if (m_harmony) {
     216           85 :         delete m_harmony;
     217              :     }
     218          300 : }
     219              : 
     220              : //---------------------------------------------------------
     221              : //   linkedClone
     222              : //---------------------------------------------------------
     223              : 
     224            0 : EngravingItem* FretDiagram::linkedClone()
     225              : {
     226            0 :     FretDiagram* e = clone();
     227            0 :     e->setAutoplace(true);
     228            0 :     if (m_harmony) {
     229            0 :         EngravingItem* newHarmony = m_harmony->linkedClone();
     230            0 :         e->add(newHarmony);
     231              :     }
     232            0 :     score()->undo(new Link(e, this));
     233            0 :     return e;
     234              : }
     235              : 
     236          701 : Segment* FretDiagram::segment() const
     237              : {
     238          701 :     EngravingObject* parent = ownershipParent();
     239          701 :     if (!parent || !parent->isSegment()) {
     240            0 :         return nullptr;
     241              :     }
     242              : 
     243          701 :     return toSegment(ownershipParent());
     244              : }
     245              : 
     246           69 : void FretDiagram::updateDiagram(const String& harmonyName)
     247              : {
     248           69 :     std::vector<DiagramInfo> availableDiagrams = patternsFromHarmony(harmonyName);
     249           69 :     if (availableDiagrams.empty()) {
     250            0 :         return;
     251              :     }
     252              : 
     253           69 :     String diagramXml = resolveDiagram(availableDiagrams).diagramXml;
     254              : 
     255           69 :     if (diagramXml.empty()) {
     256            0 :         return;
     257              :     }
     258              : 
     259           69 :     clear();
     260              : 
     261           69 :     read460::ReadContext ctx(score());
     262           69 :     XmlReader reader(diagramXml.toUtf8());
     263              : 
     264           69 :     read460::TRead::read(this, reader, ctx);
     265              : 
     266           69 :     triggerLayout();
     267           69 :     return;
     268           69 : }
     269              : 
     270         1391 : double FretDiagram::mainWidth() const
     271              : {
     272         1391 :     double mainWidth = 0.0;
     273         1391 :     if (orientation() == Orientation::VERTICAL) {
     274         1391 :         mainWidth = ldata()->stringDist * (strings() - 1);
     275            0 :     } else if (orientation() == Orientation::HORIZONTAL) {
     276            0 :         mainWidth = ldata()->fretDist * frets();
     277              :     }
     278         1391 :     return mainWidth;
     279              : }
     280              : 
     281              : //---------------------------------------------------------
     282              : //   dragAnchorLines
     283              : //---------------------------------------------------------
     284              : 
     285            0 : std::vector<LineF> FretDiagram::dragAnchorLines() const
     286              : {
     287            0 :     return genericDragAnchorLines();
     288              : }
     289              : 
     290              : //---------------------------------------------------------
     291              : //   setStrings
     292              : //---------------------------------------------------------
     293              : 
     294          166 : void FretDiagram::setStrings(int n)
     295              : {
     296          166 :     int difference = n - m_strings;
     297          166 :     if (difference == 0 || n <= 0) {
     298          166 :         return;
     299              :     }
     300              : 
     301              :     // Move all dots, makers, barres to the RIGHT, so we add strings to the left
     302              :     // This is more useful - few instruments need strings added to the right.
     303            0 :     DotMap tempDots;
     304            0 :     MarkerMap tempMarkers;
     305              : 
     306            0 :     for (int string = 0; string < m_strings; ++string) {
     307            0 :         if (string + difference < 0) {
     308            0 :             continue;
     309              :         }
     310              : 
     311            0 :         for (auto const& d : dot(string)) {
     312            0 :             if (d.exists()) {
     313            0 :                 tempDots[string + difference].push_back(FretItem::Dot(d));
     314              :             }
     315            0 :         }
     316              : 
     317            0 :         if (marker(string).exists()) {
     318            0 :             tempMarkers[string + difference] = marker(string);
     319              :         }
     320              :     }
     321              : 
     322            0 :     m_dots = tempDots;
     323            0 :     m_markers = tempMarkers;
     324              : 
     325            0 :     for (int fret = 1; fret <= m_frets; ++fret) {
     326            0 :         if (barre(fret).exists()) {
     327            0 :             if (m_barres[fret].startString + difference <= 0) {
     328            0 :                 removeBarre(fret);
     329            0 :                 continue;
     330              :             }
     331              : 
     332            0 :             m_barres[fret].startString = std::max(0, m_barres[fret].startString + difference);
     333            0 :             m_barres[fret].endString   = m_barres[fret].endString == -1 ? -1 : m_barres[fret].endString + difference;
     334              :         }
     335              :     }
     336              : 
     337            0 :     if (difference > 0) {
     338            0 :         for (int i = 0; i < difference; ++i) {
     339            0 :             m_fingering.push_back(0);
     340              :         }
     341              :     } else {
     342            0 :         m_fingering.erase(m_fingering.end() + difference, m_fingering.end());
     343              :     }
     344              : 
     345            0 :     m_strings = n;
     346            0 : }
     347              : 
     348              : //---------------------------------------------------------
     349              : //   init
     350              : //---------------------------------------------------------
     351              : 
     352            0 : void FretDiagram::init(StringData* stringData, Chord* chord)
     353              : {
     354            0 :     if (!stringData) {
     355            0 :         setStrings(6);
     356              :     } else {
     357            0 :         setStrings(static_cast<int>(stringData->strings()));
     358              :     }
     359            0 :     if (stringData) {
     360            0 :         for (int string = 0; string < m_strings; ++string) {
     361            0 :             setMarker(string, FretMarkerType::CROSS);
     362              :         }
     363            0 :         for (const Note* note : chord->notes()) {
     364              :             int string;
     365              :             int fret;
     366            0 :             if (stringData->convertPitch(note->pitch(), chord->staff(), &string, &fret)) {
     367            0 :                 setDot(string, fret);
     368              :             }
     369              :         }
     370            0 :         m_frets = stringData->frets();
     371              :     } else {
     372            0 :         m_maxFrets = 6;
     373              :     }
     374            0 : }
     375              : 
     376              : //---------------------------------------------------------
     377              : //   setDot
     378              : //    take a fret value of 0 to mean remove the dot, except with add
     379              : //    where we actually need to pass a fret val.
     380              : //---------------------------------------------------------
     381              : 
     382          456 : void FretDiagram::setDot(int string, int fret, bool add /*= false*/, FretDotType dtype /*= FretDotType::NORMAL*/)
     383              : {
     384          456 :     if (fret == 0) {
     385            0 :         removeDot(string, fret);
     386          456 :     } else if (string >= 0 && string < m_strings) {
     387              :         // Special case - with add, if there is a dot in the position, remove it
     388              :         // If not, add it.
     389          456 :         if (add) {
     390          369 :             if (dot(string, fret)[0].exists()) {
     391            0 :                 removeDot(string, fret);
     392            0 :                 return;             // We are done here, all we needed to do was remove a single dot
     393              :             }
     394              :         } else {
     395           87 :             m_dots[string].clear();
     396              :         }
     397              : 
     398          456 :         m_dots[string].push_back(FretItem::Dot(fret, dtype));
     399          456 :         if (!add) {
     400           87 :             setMarker(string, FretMarkerType::NONE);
     401              :         }
     402              :     }
     403              : }
     404              : 
     405            0 : void FretDiagram::addDotForDotStyleBarre(int string, int fret)
     406              : {
     407            0 :     if (m_dots[string].empty()) {
     408            0 :         m_dots[string].push_back(FretItem::Dot(fret, FretDotType::NORMAL, /*isPartOfSlurBarre*/ true));
     409              :     }
     410            0 : }
     411              : 
     412         1139 : void FretDiagram::removeDotForDotStyleBarre(int string, int fret)
     413              : {
     414         1139 :     removeDot(string, fret);
     415         1139 : }
     416              : 
     417              : //---------------------------------------------------------
     418              : //   setMarker
     419              : //    Removal of dots and barres if "Multiple dots" is inactive
     420              : //    is handled in FretCanvas::mousePressEvent()
     421              : //---------------------------------------------------------
     422              : 
     423          554 : void FretDiagram::setMarker(int string, FretMarkerType mtype)
     424              : {
     425          554 :     if (string >= 0 && string < m_strings) {
     426          554 :         m_markers[string] = FretItem::Marker(mtype);
     427          554 :         if (mtype != FretMarkerType::NONE) {
     428          431 :             removeDot(string);
     429          431 :             removeBarres(string);
     430              :         }
     431              :     }
     432          554 : }
     433              : 
     434              : //---------------------------------------------------------
     435              : //   setBarre
     436              : //    We'll accept a value of -1 for the end string, to denote
     437              : //    that the barre goes as far right as possible.
     438              : //    Take a start string value of -1 to mean 'remove this barre'
     439              : //---------------------------------------------------------
     440              : 
     441           35 : void FretDiagram::setBarre(int startString, int endString, int fret)
     442              : {
     443           35 :     if (startString == -1) {
     444            0 :         removeBarre(fret);
     445           35 :     } else if (startString >= 0 && endString >= -1 && startString < m_strings && endString < m_strings) {
     446           35 :         m_barres[fret] = FretItem::Barre(startString, endString);
     447              :     }
     448           35 : }
     449              : 
     450              : //---------------------------------------------------------
     451              : //    This version is for clicks on a dot with shift.
     452              : //    If there is no barre at fret, then add one with the string as the start.
     453              : //    If there is a barre with a -1 end string, set the end string to string.
     454              : //    If there is a barre with a set start and end, remove it.
     455              : //    Add may be used in the future if we decide to add dots as default with barres.
     456              : //---------------------------------------------------------
     457              : 
     458            1 : void FretDiagram::setBarre(int string, int fret, bool add /*= false*/)
     459              : {
     460              :     UNUSED(add);
     461              : 
     462            1 :     FretItem::Barre b = barre(fret);
     463            1 :     if (!b.exists()) {
     464            1 :         if (string < m_strings - 1) {
     465            1 :             m_barres[fret] = FretItem::Barre(string, -1);
     466            1 :             removeDotsMarkers(string, -1, fret);
     467              :         }
     468            0 :     } else if (b.endString == -1 && b.startString < string) {
     469            0 :         m_barres[fret].endString = string;
     470              :     } else {
     471            0 :         removeDotsMarkers(b.startString, b.endString, fret);
     472            0 :         removeBarre(fret);
     473              :     }
     474            1 : }
     475              : 
     476              : //---------------------------------------------------------
     477              : //   undoSetFretDot
     478              : //---------------------------------------------------------
     479              : 
     480            1 : void FretDiagram::undoSetFretDot(int _string, int _fret, bool _add /*= true*/, FretDotType _dtype /*= FretDotType::NORMAl*/)
     481              : {
     482            2 :     for (EngravingObject* e : linkList()) {
     483            1 :         FretDiagram* fd = toFretDiagram(e);
     484            1 :         fd->score()->undo(new FretDot(fd, _string, _fret, _add, _dtype));
     485            1 :     }
     486            1 : }
     487              : 
     488              : //---------------------------------------------------------
     489              : //   undoSetFretMarker
     490              : //---------------------------------------------------------
     491              : 
     492            1 : void FretDiagram::undoSetFretMarker(int _string, FretMarkerType _mtype)
     493              : {
     494            2 :     for (EngravingObject* e : linkList()) {
     495            1 :         FretDiagram* fd = toFretDiagram(e);
     496            1 :         fd->score()->undo(new FretMarker(fd, _string, _mtype));
     497            1 :     }
     498            1 : }
     499              : 
     500              : //---------------------------------------------------------
     501              : //   undoSetFretBarre
     502              : //    add refers to using multiple dots per string when adding dots automatically
     503              : //---------------------------------------------------------
     504              : 
     505            1 : void FretDiagram::undoSetFretBarre(int _string, int _fret, bool _add /*= false*/)
     506              : {
     507            2 :     for (EngravingObject* e : linkList()) {
     508            1 :         FretDiagram* fd = toFretDiagram(e);
     509            1 :         fd->score()->undo(new FretBarre(fd, _string, _fret, _add));
     510            1 :     }
     511            1 : }
     512              : 
     513              : //---------------------------------------------------------
     514              : //   removeBarre
     515              : //    Remove a barre on a given fret.
     516              : //---------------------------------------------------------
     517              : 
     518            0 : void FretDiagram::removeBarre(int f)
     519              : {
     520            0 :     m_barres.erase(f);
     521            0 : }
     522              : 
     523              : //---------------------------------------------------------
     524              : //   removeBarres
     525              : //    Remove barres crossing a certain point. Fret of 0 means any point along
     526              : //    the string.
     527              : //---------------------------------------------------------
     528              : 
     529          431 : void FretDiagram::removeBarres(int string, int fret /*= 0*/)
     530              : {
     531          431 :     auto iter = m_barres.begin();
     532          431 :     while (iter != m_barres.end()) {
     533            0 :         int bfret = iter->first;
     534            0 :         FretItem::Barre b = iter->second;
     535              : 
     536            0 :         if (b.exists() && b.startString <= string && (b.endString >= string || b.endString == -1)) {
     537            0 :             if (fret > 0 && fret != bfret) {
     538            0 :                 ++iter;
     539              :             } else {
     540            0 :                 iter = m_barres.erase(iter);
     541              :             }
     542              :         } else {
     543            0 :             ++iter;
     544              :         }
     545              :     }
     546          431 : }
     547              : 
     548              : //---------------------------------------------------------
     549              : //   removeMarker
     550              : //---------------------------------------------------------
     551              : 
     552            0 : void FretDiagram::removeMarker(int s)
     553              : {
     554            0 :     auto it = m_markers.find(s);
     555            0 :     m_markers.erase(it);
     556            0 : }
     557              : 
     558              : //---------------------------------------------------------
     559              : //   removeDot
     560              : //    take a fret value of 0 to mean remove all dots
     561              : //---------------------------------------------------------
     562              : 
     563         1577 : void FretDiagram::removeDot(int s, int f /*= 0*/)
     564              : {
     565         1577 :     if (f > 0) {
     566         1146 :         std::vector<FretItem::Dot> tempDots;
     567         2292 :         for (auto const& d : dot(s)) {
     568         1146 :             if (d.exists() && d.fret != f) {
     569          615 :                 tempDots.push_back(FretItem::Dot(d));
     570              :             }
     571         1146 :         }
     572              : 
     573         1146 :         m_dots[s] = tempDots;
     574         1146 :     } else {
     575          431 :         m_dots[s].clear();
     576              :     }
     577              : 
     578         1577 :     if (m_dots[s].size() == 0) {
     579          962 :         auto it = m_dots.find(s);
     580          962 :         m_dots.erase(it);
     581              :     }
     582         1577 : }
     583              : 
     584              : //---------------------------------------------------------
     585              : //   removeDotsMarkers
     586              : //    removes all markers between [ss, es] and dots between [ss, es],
     587              : //    where the dots have a fret of fret.
     588              : //---------------------------------------------------------
     589              : 
     590            1 : void FretDiagram::removeDotsMarkers(int ss, int es, int fret)
     591              : {
     592            1 :     if (ss == -1) {
     593            0 :         return;
     594              :     }
     595              : 
     596            1 :     int end = es == -1 ? m_strings : es;
     597            8 :     for (int string = ss; string <= end; ++string) {
     598            7 :         removeDot(string, fret);
     599              : 
     600            7 :         if (marker(string).exists()) {
     601            0 :             removeMarker(string);
     602              :         }
     603              :     }
     604              : }
     605              : 
     606              : //---------------------------------------------------------
     607              : //   Fill diagram from string like "XO-[1-O][1-X,2-O,3-S][3-T];B1[0-5];B..."
     608              : //   - Each character or bracketed block represents a string, from lowest to highest.
     609              : //   - 'X' = muted string (cross marker)
     610              : //   - 'O' = open string (circle marker)
     611              : //   - '-' = empty or unused string
     612              : //   - [fret-type,...] = one or more fretted dots on that string:
     613              : //       • fret is absolute (already includes offset)
     614              : //       • type is:
     615              : //           - O = circle
     616              : //           - X = cross
     617              : //           - S = square
     618              : //           - T = triangle
     619              : //   - Example: [1-X,2-O,3-S] means three dots on frets 1, 2, and 3 with different types
     620              : //   - Barre chords follow after ';', in the format: B{fret}[{start}-{end}]
     621              : //     e.g. B1[0-5] = barre on fret 1 from string 0 to 5
     622              : //---------------------------------------------------------
     623              : 
     624            0 : void FretDiagram::applyDiagramPattern(FretDiagram* diagram, const String& pattern)
     625              : {
     626            0 :     diagram->clear();
     627              : 
     628            0 :     const std::vector<String> parts = pattern.split(';');
     629            0 :     if (parts.empty()) {
     630            0 :         return;
     631              :     }
     632              : 
     633            0 :     const String& mainPart = parts[0];
     634            0 :     std::vector<String> stringTokens;
     635              : 
     636            0 :     for (size_t i = 0; i < mainPart.size();) {
     637            0 :         if (mainPart[i] == u'[') {
     638            0 :             size_t rb = mainPart.indexOf(u']', i);
     639            0 :             if (rb != muse::nidx) {
     640            0 :                 stringTokens.push_back(mainPart.mid(i, rb - i + 1));
     641            0 :                 i = rb + 1;
     642            0 :                 continue;
     643              :             }
     644              :         }
     645            0 :         stringTokens.push_back(mainPart.mid(i, 1));
     646            0 :         ++i;
     647              :     }
     648              : 
     649            0 :     const int stringCount = static_cast<int>(stringTokens.size());
     650            0 :     diagram->setStrings(stringCount);
     651            0 :     diagram->setFrets(4);
     652            0 :     diagram->setPropertyFlags(Pid::FRET_STRINGS, PropertyFlags::UNSTYLED);
     653            0 :     diagram->setPropertyFlags(Pid::FRET_FRETS,   PropertyFlags::UNSTYLED);
     654              : 
     655            0 :     int offset = 0;
     656              : 
     657            0 :     for (int i = 0; i < stringCount; ++i) {
     658            0 :         const String& token = stringTokens[i];
     659              : 
     660            0 :         if (token == u"-") {
     661            0 :             continue;
     662              :         }
     663              : 
     664            0 :         if (token.startsWith(u"[") && token.endsWith(u"]")) {
     665              :             // Example: [1-O,2-X,3-S]
     666            0 :             String inner = token.mid(1, token.size() - 2);
     667            0 :             std::vector<String> pairs = inner.split(u',');
     668              : 
     669            0 :             for (const String& p : pairs) {
     670            0 :                 size_t dash = p.indexOf(u'-');
     671            0 :                 if (dash > 0) {
     672            0 :                     int fret = p.left(dash).toInt();
     673            0 :                     Char typeChar = p.mid(dash + 1, 1).at(0);
     674              :                     FretDotType dt;
     675              : 
     676            0 :                     switch (typeChar.unicode()) {
     677            0 :                     case 'X': dt = FretDotType::CROSS;
     678            0 :                         break;
     679            0 :                     case 'O': dt = FretDotType::NORMAL;
     680            0 :                         break;
     681            0 :                     case 'S': dt = FretDotType::SQUARE;
     682            0 :                         break;
     683            0 :                     case 'T': dt = FretDotType::TRIANGLE;
     684            0 :                         break;
     685            0 :                     default:  dt = FretDotType::NORMAL;
     686            0 :                         break;
     687              :                     }
     688              : 
     689            0 :                     diagram->setDot(i, fret - offset, true, dt);
     690            0 :                     if (fret - 3 > 0 && offset < fret - 3) {
     691            0 :                         offset = fret - 3;
     692              :                     }
     693              :                 }
     694              :             }
     695            0 :         } else if (token == u"X") {
     696            0 :             diagram->setMarker(i, FretMarkerType::CROSS);
     697            0 :         } else if (token == u"O") {
     698            0 :             diagram->setMarker(i, FretMarkerType::CIRCLE);
     699              :         }
     700              :     }
     701              : 
     702            0 :     if (offset > 0) {
     703            0 :         diagram->setFretOffset(offset);
     704              :     }
     705              : 
     706            0 :     for (size_t i = 1; i < parts.size(); ++i) {
     707            0 :         const String& barrePart = parts[i];
     708            0 :         if (!barrePart.startsWith(u'B')) {
     709            0 :             continue;
     710              :         }
     711              : 
     712            0 :         size_t lb = barrePart.indexOf(u'[');
     713            0 :         size_t rb = barrePart.indexOf(u']');
     714            0 :         if (lb < 2 || rb < lb) {
     715            0 :             continue;
     716              :         }
     717              : 
     718            0 :         int fret = barrePart.mid(1, lb - 1).toInt();
     719            0 :         if (fret <= 0) {
     720            0 :             continue;
     721              :         }
     722              : 
     723            0 :         String range = barrePart.mid(lb + 1, rb - lb - 1);
     724            0 :         size_t dash = range.indexOf('-');
     725            0 :         if (dash > 0) {
     726            0 :             int start = range.left(dash).toInt();
     727            0 :             int end = range.mid(dash + 1).toInt();
     728            0 :             if (start >= 0 && end >= start && end < stringCount) {
     729            0 :                 diagram->setBarre(start, end, fret - offset);
     730              :             }
     731              :         }
     732            0 :     }
     733            0 : }
     734              : 
     735           56 : String FretDiagram::patternFromDiagram() const
     736              : {
     737           56 :     const int diagramStrings = strings();
     738           56 :     const int offset = fretOffset();
     739              : 
     740           56 :     StringList patternParts;
     741           56 :     const DotMap& dotsMap = dots();
     742              : 
     743          392 :     for (int i = 0; i < diagramStrings; ++i) {
     744          336 :         const FretItem::Marker fretMarker = marker(i);
     745              : 
     746          336 :         if (fretMarker.mtype == FretMarkerType::CROSS) {
     747           25 :             patternParts.push_back(u"X");
     748           79 :             continue;
     749          311 :         } else if (fretMarker.mtype == FretMarkerType::CIRCLE) {
     750           54 :             patternParts.push_back(u"O");
     751           54 :             continue;
     752              :         }
     753              : 
     754          257 :         const auto it = dotsMap.find(i);
     755          257 :         std::vector<FretItem::Dot> dotList;
     756          257 :         if (it != dotsMap.end()) {
     757           75 :             const std::vector<FretItem::Dot>& dots = it->second;
     758          150 :             for (const FretItem::Dot& dot : dots) {
     759           75 :                 if (!dot.isPartOfSlurBarre) { // Don't write dot if part of slur barré
     760           75 :                     dotList.push_back(dot);
     761              :                 }
     762              :             }
     763              :         }
     764              : 
     765          257 :         if (!dotList.empty()) {
     766           75 :             const auto& dotList2 = it->second;
     767           75 :             StringList dotDescriptions;
     768          150 :             for (const auto& dot : dotList2) {
     769           75 :                 if (dot.isPartOfSlurBarre) {
     770            0 :                     continue;
     771              :                 }
     772              : 
     773           75 :                 int actualFret = dot.fret + offset;
     774           75 :                 Char typeChar = u'O';
     775              : 
     776           75 :                 switch (dot.dtype) {
     777           75 :                 case FretDotType::NORMAL:   typeChar = u'O';
     778           75 :                     break;
     779            0 :                 case FretDotType::CROSS:    typeChar = u'X';
     780            0 :                     break;
     781            0 :                 case FretDotType::SQUARE:   typeChar = u'S';
     782            0 :                     break;
     783            0 :                 case FretDotType::TRIANGLE: typeChar = u'T';
     784            0 :                     break;
     785            0 :                 default: break;
     786              :                 }
     787              : 
     788           75 :                 dotDescriptions.push_back(String::number(actualFret) + u'-' + typeChar);
     789              :             }
     790              : 
     791           75 :             patternParts.push_back(u'[' + dotDescriptions.join(u",") + u']');
     792           75 :         } else {
     793          182 :             patternParts.push_back(u"-");
     794              :         }
     795          257 :     }
     796              : 
     797           56 :     String pattern = patternParts.join(u"");
     798              : 
     799           56 :     const BarreMap& _barres = barres();
     800            0 :     StringList barreParts;
     801           63 :     for (const auto& [fret, b] : _barres) {
     802            7 :         if (!b.exists()) {
     803            0 :             continue;
     804              :         }
     805              : 
     806            7 :         int adjustedFret = fret + offset;
     807            7 :         int start = b.startString;
     808            7 :         int end = (b.endString != -1) ? b.endString : diagramStrings - 1;
     809              : 
     810           21 :         barreParts.push_back(u'B' + String::number(adjustedFret)
     811           28 :                              + u'[' + String::number(start)
     812           28 :                              + u'-' + String::number(end) + u']');
     813              :     }
     814              : 
     815           56 :     if (!barreParts.empty()) {
     816            7 :         pattern += u';' + barreParts.join(u";");
     817              :     }
     818              : 
     819          112 :     return pattern;
     820           56 : }
     821              : 
     822           16 : std::vector<String> FretDiagram::harmoniesFromPattern(const String& pattern) const
     823              : {
     824           16 :     if (s_diagramPatternToHarmoniesMap.empty()) {
     825            0 :         readHarmonyToDiagramFile(HARMONY_TO_DIAGRAM_FILE_PATH);
     826              :     }
     827           16 :     return muse::value(s_diagramPatternToHarmoniesMap, pattern);
     828              : }
     829              : 
     830          105 : std::vector<DiagramInfo> FretDiagram::patternsFromHarmony(const String& harmonyName)
     831              : {
     832          105 :     if (s_harmonyToDiagramMap.empty()) {
     833            0 :         readHarmonyToDiagramFile(HARMONY_TO_DIAGRAM_FILE_PATH);
     834              :     }
     835              : 
     836          105 :     NoteSpellingType spellingType = style().styleV(Sid::chordSymbolSpelling).value<NoteSpellingType>();
     837          105 :     HarmonyMapKey key = createHarmonyMapKey(harmonyName, spellingType, score()->chordList());
     838              : 
     839          105 :     if (tpcIsValid(key.rootTpc)) {
     840          105 :         key.rootTpc = canonicalEnharmonicTpc(key.rootTpc);
     841              :     }
     842          105 :     if (tpcIsValid(key.bassTpc)) {
     843            2 :         key.bassTpc = canonicalEnharmonicTpc(key.bassTpc);
     844              :     }
     845              : 
     846          210 :     return muse::value(s_harmonyToDiagramMap, key);
     847              : }
     848              : 
     849              : //---------------------------------------------------------
     850              : //   clear
     851              : //---------------------------------------------------------
     852              : 
     853           70 : void FretDiagram::clear()
     854              : {
     855           70 :     m_barres.clear();
     856           70 :     m_dots.clear();
     857           70 :     m_markers.clear();
     858           70 :     m_fretOffset = 0;
     859           70 : }
     860              : 
     861            3 : bool FretDiagram::isClear() const
     862              : {
     863            3 :     return m_barres.empty() && m_dots.empty() && m_markers.empty();
     864              : }
     865              : 
     866              : //---------------------------------------------------------
     867              : //   undoFretClear
     868              : //---------------------------------------------------------
     869              : 
     870            1 : void FretDiagram::undoFretClear()
     871              : {
     872            2 :     for (EngravingObject* e : linkList()) {
     873            1 :         FretDiagram* fd = toFretDiagram(e);
     874            1 :         fd->score()->undo(new FretClear(fd));
     875            1 :     }
     876            1 : }
     877              : 
     878           80 : int FretDiagram::numPos() const
     879              : {
     880           80 :     return style().styleI(Sid::fretNumPos);
     881              : }
     882              : 
     883              : //---------------------------------------------------------
     884              : //   dot
     885              : //    take fret value of zero to mean all dots
     886              : //---------------------------------------------------------
     887              : 
     888         2036 : std::vector<FretItem::Dot> FretDiagram::dot(int s, int f /*= 0*/) const
     889              : {
     890         2036 :     if (m_dots.find(s) != m_dots.end()) {
     891          880 :         if (f != 0) {
     892            0 :             for (auto const& d : m_dots.at(s)) {
     893            0 :                 if (d.fret == f) {
     894            0 :                     return std::vector<FretItem::Dot> { FretItem::Dot(d) };
     895              :                 }
     896              :             }
     897              :         } else {
     898          880 :             return m_dots.at(s);
     899              :         }
     900              :     }
     901         3468 :     return std::vector<FretItem::Dot> { FretItem::Dot(0) };
     902              : }
     903              : 
     904              : //---------------------------------------------------------
     905              : //   marker
     906              : //---------------------------------------------------------
     907              : 
     908          954 : FretItem::Marker FretDiagram::marker(int s) const
     909              : {
     910          954 :     if (m_markers.find(s) != m_markers.end()) {
     911          393 :         return m_markers.at(s);
     912              :     }
     913          561 :     return FretItem::Marker(FretMarkerType::NONE);
     914              : }
     915              : 
     916              : //---------------------------------------------------------
     917              : //   barre
     918              : //---------------------------------------------------------
     919              : 
     920          274 : FretItem::Barre FretDiagram::barre(int f) const
     921              : {
     922          274 :     if (m_barres.find(f) != m_barres.end()) {
     923           26 :         return m_barres.at(f);
     924              :     }
     925          248 :     return FretItem::Barre(-1, -1);
     926              : }
     927              : 
     928           44 : Font FretDiagram::fretNumFont() const
     929              : {
     930           44 :     const MStyle& st = style();
     931           44 :     Font f(st.styleSt(Sid::fretDiagramFretNumberFontFace), Font::Type::Text);
     932           44 :     f.setPointSizeF(st.styleD(Sid::fretDiagramFretNumberFontSize) * userMag());
     933           44 :     FontStyle fStyle = st.styleV(Sid::fretDiagramFretNumberFontStyle).value<FontStyle>();
     934           44 :     f.setBold(fStyle & FontStyle::Bold);
     935           44 :     f.setItalic(fStyle & FontStyle::Italic);
     936           44 :     f.setUnderline(fStyle & FontStyle::Underline);
     937           44 :     f.setStrike(fStyle & FontStyle::Strike);
     938           44 :     return f;
     939            0 : }
     940              : 
     941            0 : Font FretDiagram::fingeringFont() const
     942              : {
     943            0 :     const MStyle& st = style();
     944            0 :     Font f(st.styleSt(Sid::fretDiagramFingeringFontFace), Font::Type::Text);
     945            0 :     f.setPointSizeF(st.styleD(Sid::fretDiagramFingeringFontSize) * userMag());
     946            0 :     FontStyle fStyle = st.styleV(Sid::fretDiagramFingeringFontStyle).value<FontStyle>();
     947            0 :     f.setBold(fStyle & FontStyle::Bold);
     948            0 :     f.setItalic(fStyle & FontStyle::Italic);
     949            0 :     f.setUnderline(fStyle & FontStyle::Underline);
     950            0 :     f.setStrike(fStyle & FontStyle::Strike);
     951            0 :     return f;
     952            0 : }
     953              : 
     954           41 : String FretDiagram::harmonyPlainText() const
     955              : {
     956           41 :     return m_harmony ? m_harmony->plainText() : String();
     957              : }
     958              : 
     959          211 : String FretDiagram::harmonyDisplayText() const
     960              : {
     961          211 :     return m_harmony ? m_harmony->displayText() : String();
     962              : }
     963              : 
     964              : //---------------------------------------------------------
     965              : //   setHarmony
     966              : ///   if this is being done by the user, use undoSetHarmony instead
     967              : //---------------------------------------------------------
     968              : 
     969           27 : void FretDiagram::setHarmony(String harmonyText)
     970              : {
     971           27 :     if (!m_harmony) {
     972           27 :         Harmony* h = new Harmony(this);
     973           27 :         add(h);
     974              :     }
     975              : 
     976           27 :     m_harmony->setHarmony(harmonyText);
     977           27 :     m_harmony->setXmlText(m_harmony->harmonyName());
     978           27 :     triggerLayout();
     979           27 : }
     980              : 
     981              : //---------------------------------------------------------
     982              : //   add
     983              : //---------------------------------------------------------
     984              : 
     985           85 : void FretDiagram::add(EngravingItem* e)
     986              : {
     987           85 :     e->setOwnershipParent(this);
     988           85 :     if (e->isHarmony()) {
     989           85 :         m_harmony = toHarmony(e);
     990              : 
     991           85 :         m_harmony->setTrack(track());
     992              : 
     993           85 :         if (m_harmony->harmonyName().empty()) {
     994           28 :             if (s_diagramPatternToHarmoniesMap.empty()) {
     995            2 :                 readHarmonyToDiagramFile(HARMONY_TO_DIAGRAM_FILE_PATH);
     996              :             }
     997              : 
     998           28 :             String pattern = patternFromDiagram();
     999           28 :             if (!pattern.empty()) {
    1000           28 :                 std::vector<String> matchedHarmonies = muse::value(s_diagramPatternToHarmoniesMap, pattern);
    1001           28 :                 if (!matchedHarmonies.empty()) {
    1002            1 :                     m_harmony->setHarmony(matchedHarmonies.front());
    1003              :                 }
    1004           28 :             }
    1005           28 :         }
    1006              : 
    1007           85 :         e->added();
    1008              :     } else {
    1009            0 :         LOGW("FretDiagram: cannot add <%s>\n", e->typeName());
    1010              :     }
    1011           85 : }
    1012              : 
    1013              : //---------------------------------------------------------
    1014              : //   remove
    1015              : //---------------------------------------------------------
    1016              : 
    1017            0 : void FretDiagram::remove(EngravingItem* e)
    1018              : {
    1019            0 :     if (e == m_harmony) {
    1020            0 :         m_harmony = nullptr;
    1021            0 :         e->removed();
    1022              :     } else {
    1023            0 :         LOGW("FretDiagram: cannot remove <%s>\n", e->typeName());
    1024              :     }
    1025            0 : }
    1026              : 
    1027            0 : RectF FretDiagram::drag(EditData& ed)
    1028              : {
    1029            0 :     RectF result = EngravingItem::drag(ed);
    1030              : 
    1031            0 :     MoveElementAnchors::moveElementAnchorsOnDrag(this, ed);
    1032              : 
    1033            0 :     return result;
    1034              : }
    1035              : 
    1036              : //---------------------------------------------------------
    1037              : //   acceptDrop
    1038              : //---------------------------------------------------------
    1039              : 
    1040            0 : bool FretDiagram::acceptDrop(EditData& data) const
    1041              : {
    1042            0 :     return data.dropElement->isHarmony();
    1043              : }
    1044              : 
    1045              : //---------------------------------------------------------
    1046              : //   drop
    1047              : //---------------------------------------------------------
    1048              : 
    1049            0 : EngravingItem* FretDiagram::drop(Transaction&, EditData& data)
    1050              : {
    1051            0 :     EngravingItem* e = data.dropElement;
    1052            0 :     if (e->isHarmony()) {
    1053            0 :         Harmony* h = toHarmony(e);
    1054            0 :         h->setOwnershipParent(ownershipParent());
    1055            0 :         h->setTrack(track());
    1056            0 :         score()->undoAddElement(h);
    1057              :     } else {
    1058            0 :         LOGW("FretDiagram: cannot drop <%s>\n", e->typeName());
    1059            0 :         delete e;
    1060            0 :         e = 0;
    1061              :     }
    1062            0 :     return e;
    1063              : }
    1064              : 
    1065              : //---------------------------------------------------------
    1066              : //   scanElements
    1067              : //---------------------------------------------------------
    1068              : 
    1069            9 : void FretDiagram::scanElements(std::function<void(EngravingItem*)> func)
    1070              : {
    1071            9 :     func(this);
    1072              : 
    1073              :     // don't display harmony in palette
    1074            9 :     if (m_harmony && !score()->isPaletteScore()) {
    1075            2 :         func(m_harmony);
    1076              :     }
    1077            9 : }
    1078              : 
    1079              : //---------------------------------------------------------
    1080              : //   getProperty
    1081              : //---------------------------------------------------------
    1082              : 
    1083            1 : void FretDiagram::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
    1084              : {
    1085            1 :     if (id == Pid::EXCLUDE_VERTICAL_ALIGN) {
    1086            0 :         EngravingItem::undoChangeProperty(id, v, ps);
    1087              : 
    1088            0 :         bool val = v.toBool();
    1089            0 :         Harmony* h = harmony();
    1090            0 :         if (h && h->excludeVerticalAlign() != val) {
    1091            0 :             h->undoChangeProperty(Pid::EXCLUDE_VERTICAL_ALIGN, val, ps);
    1092              :         }
    1093            0 :         Segment* parentSeg = segment();
    1094            0 :         if (!parentSeg) {
    1095            0 :             return;
    1096              :         }
    1097              : 
    1098            0 :         for (EngravingItem* item : parentSeg->annotations()) {
    1099            0 :             if ((!item->isFretDiagram() && !item->isHarmony()) || item == this || track2staff(item->track()) != staffIdx()) {
    1100            0 :                 continue;
    1101              :             }
    1102              : 
    1103            0 :             if (item->excludeVerticalAlign() != val) {
    1104            0 :                 item->undoChangeProperty(Pid::EXCLUDE_VERTICAL_ALIGN, val, ps);
    1105              :             }
    1106              :         }
    1107            0 :         return;
    1108              :     }
    1109              : 
    1110            1 :     EngravingItem::undoChangeProperty(id, v, ps);
    1111              : }
    1112              : 
    1113          745 : PropertyValue FretDiagram::getProperty(Pid propertyId) const
    1114              : {
    1115          745 :     switch (propertyId) {
    1116            0 :     case Pid::MAG:
    1117            0 :         return userMag();
    1118            0 :     case Pid::FRET_STRINGS:
    1119            0 :         return strings();
    1120            0 :     case Pid::FRET_FRETS:
    1121            0 :         return frets();
    1122            0 :     case Pid::FRET_NUT:
    1123            0 :         return showNut();
    1124           74 :     case Pid::FRET_OFFSET:
    1125           74 :         return fretOffset();
    1126            0 :     case Pid::ORIENTATION:
    1127            0 :         return m_orientation;
    1128            0 :     case Pid::FRET_SHOW_FINGERINGS:
    1129            0 :         return m_showFingering;
    1130           67 :     case Pid::FRET_FINGERING:
    1131           67 :         return m_fingering;
    1132          604 :     default:
    1133          604 :         return EngravingItem::getProperty(propertyId);
    1134              :     }
    1135              : }
    1136              : 
    1137              : //---------------------------------------------------------
    1138              : //   setProperty
    1139              : //---------------------------------------------------------
    1140              : 
    1141         1438 : bool FretDiagram::setProperty(Pid propertyId, const PropertyValue& v)
    1142              : {
    1143         1438 :     switch (propertyId) {
    1144          156 :     case Pid::MAG:
    1145          156 :         setUserMag(v.toDouble());
    1146          156 :         break;
    1147          156 :     case Pid::FRET_STRINGS:
    1148          156 :         setStrings(v.toInt());
    1149          156 :         break;
    1150          179 :     case Pid::FRET_FRETS:
    1151          179 :         setFrets(v.toInt());
    1152          179 :         break;
    1153          156 :     case Pid::FRET_NUT:
    1154          156 :         setShowNut(v.toBool());
    1155          156 :         break;
    1156           11 :     case Pid::FRET_OFFSET:
    1157           11 :         setFretOffset(v.toInt());
    1158           11 :         break;
    1159          156 :     case Pid::ORIENTATION:
    1160          156 :         m_orientation = v.value<Orientation>();
    1161          156 :         break;
    1162          156 :     case Pid::FRET_SHOW_FINGERINGS:
    1163          156 :         setShowFingering(v.toBool());
    1164          156 :         break;
    1165            0 :     case Pid::FRET_FINGERING:
    1166            0 :         setFingering(v.value<std::vector<int> >());
    1167            0 :         break;
    1168            0 :     case Pid::EXCLUDE_VERTICAL_ALIGN: {
    1169            0 :         setExcludeVerticalAlign(v.toBool());
    1170            0 :         break;
    1171              :     }
    1172          468 :     default:
    1173          468 :         return EngravingItem::setProperty(propertyId, v);
    1174              :     }
    1175          970 :     triggerLayout();
    1176          970 :     return true;
    1177              : }
    1178              : 
    1179              : //---------------------------------------------------------
    1180              : //   propertyDefault
    1181              : //---------------------------------------------------------
    1182              : 
    1183          747 : PropertyValue FretDiagram::propertyDefault(Pid pid) const
    1184              : {
    1185              :     // We shouldn't style the fret offset
    1186          747 :     if (pid == Pid::FRET_OFFSET) {
    1187           67 :         return PropertyValue(0);
    1188          680 :     } else if (pid == Pid::FRET_FINGERING) {
    1189           67 :         return std::vector<int>(m_strings, 0);
    1190              :     }
    1191              : 
    1192         6080 :     for (const StyledProperty& p : *styledProperties()) {
    1193         5477 :         if (p.pid == pid) {
    1194           10 :             if (propertyType(pid) == P_TYPE::ABSOLUTE) {
    1195           10 :                 return style().styleAbsolute(p.sid);
    1196              :             }
    1197           10 :             return style().styleV(p.sid);
    1198              :         }
    1199              :     }
    1200          603 :     return EngravingItem::propertyDefault(pid);
    1201              : }
    1202              : 
    1203          155 : void FretDiagram::setTrack(track_idx_t val)
    1204              : {
    1205          155 :     EngravingItem::setTrack(val);
    1206              : 
    1207          155 :     if (m_harmony) {
    1208           72 :         m_harmony->setTrack(val);
    1209              :     }
    1210          155 : }
    1211              : 
    1212              : //---------------------------------------------------------
    1213              : //   accessibleInfo
    1214              : //---------------------------------------------------------
    1215              : 
    1216            0 : String FretDiagram::accessibleInfo() const
    1217              : {
    1218            0 :     String chordName;
    1219            0 :     if (m_harmony) {
    1220            0 :         chordName = muse::mtrc("engraving", "with chord symbol %1").arg(m_harmony->harmonyName());
    1221              :     } else {
    1222            0 :         chordName = muse::mtrc("engraving", "without chord symbol");
    1223              :     }
    1224            0 :     return String(u"%1 %2").arg(translatedTypeUserName(), chordName);
    1225            0 : }
    1226              : 
    1227              : //---------------------------------------------------------
    1228              : //   screenReaderInfo
    1229              : //---------------------------------------------------------
    1230              : 
    1231            0 : String FretDiagram::screenReaderInfo() const
    1232              : {
    1233            0 :     String detailedInfo;
    1234            0 :     for (int i = 0; i < m_strings; i++) {
    1235            0 :         String stringIdent = muse::mtrc("engraving", "string %1").arg(i + 1);
    1236              : 
    1237            0 :         const FretItem::Marker& m = marker(i);
    1238            0 :         String markerName;
    1239            0 :         switch (m.mtype) {
    1240            0 :         case FretMarkerType::CIRCLE:
    1241            0 :             markerName = muse::mtrc("engraving", "circle marker");
    1242            0 :             break;
    1243            0 :         case FretMarkerType::CROSS:
    1244            0 :             markerName = muse::mtrc("engraving", "cross marker");
    1245            0 :             break;
    1246            0 :         case FretMarkerType::NONE:
    1247              :         default:
    1248            0 :             break;
    1249              :         }
    1250              : 
    1251            0 :         int dotsCount = 0;
    1252            0 :         std::vector<int> fretsWithDots;
    1253            0 :         for (auto const& d : dot(i)) {
    1254            0 :             if (!d.exists()) {
    1255            0 :                 continue;
    1256              :             }
    1257            0 :             fretsWithDots.push_back(d.fret + m_fretOffset);
    1258            0 :             dotsCount += 1;
    1259              :             // TODO consider: do we need to announce what type of dot a dot is?
    1260              :             // i.e. triangle, square, normal dot. It's mostly just information
    1261              :             // that clutters the screenreader output and makes it harder to
    1262              :             // understand, so leaving it out for now.
    1263            0 :         }
    1264              : 
    1265            0 :         if (dotsCount == 0 && markerName.size() == 0) {
    1266            0 :             continue;
    1267              :         }
    1268              : 
    1269            0 :         String fretInfo;
    1270            0 :         if (dotsCount == 1) {
    1271            0 :             fretInfo = String::number(fretsWithDots.front());
    1272            0 :         } else if (dotsCount > 1) {
    1273            0 :             int max = int(fretsWithDots.size());
    1274            0 :             for (int j = 0; j < max; j++) {
    1275            0 :                 if (j == max - 1) {
    1276            0 :                     fretInfo = muse::mtrc("engraving", "%1 and %2").arg(fretInfo).arg(fretsWithDots[j]);
    1277              :                 } else {
    1278            0 :                     fretInfo = String(u"%1 %2").arg(fretInfo).arg(fretsWithDots[j]);
    1279              :                 }
    1280              :             }
    1281              :         }
    1282              : 
    1283              :         //: Omit the "%n " for the singular translation (and the "(s)" too)
    1284            0 :         String dotsInfo = muse::mtrc("engraving", "%n dot(s) on fret(s) %1", "", dotsCount).arg(fretInfo);
    1285              : 
    1286            0 :         detailedInfo = String(u"%1 %2 %3 %4").arg(detailedInfo, stringIdent, markerName, dotsInfo);
    1287            0 :     }
    1288              : 
    1289            0 :     String barreInfo;
    1290            0 :     for (auto const& iter : m_barres) {
    1291            0 :         const FretItem::Barre& b = iter.second;
    1292            0 :         if (!b.exists()) {
    1293            0 :             continue;
    1294              :         }
    1295              : 
    1296            0 :         String fretInfo = muse::mtrc("engraving", "fret %1").arg(iter.first);
    1297              : 
    1298            0 :         String newBarreInfo;
    1299            0 :         if (b.startString == 0 && (b.endString == -1 || b.endString == m_strings - 1)) {
    1300            0 :             newBarreInfo = muse::mtrc("engraving", "barré %1").arg(fretInfo);
    1301              :         } else {
    1302            0 :             String startPart = muse::mtrc("engraving", "beginning string %1").arg(b.startString + 1);
    1303            0 :             String endPart;
    1304            0 :             if (b.endString != -1) {
    1305            0 :                 endPart = muse::mtrc("engraving", "and ending string %1").arg(b.endString + 1);
    1306              :             }
    1307              : 
    1308            0 :             newBarreInfo = muse::mtrc("engraving", "partial barré %1 %2 %3").arg(fretInfo, startPart, endPart);
    1309            0 :         }
    1310              : 
    1311            0 :         barreInfo = String(u"%1 %2").arg(barreInfo, newBarreInfo);
    1312            0 :     }
    1313              : 
    1314            0 :     detailedInfo = String(u"%1 %2").arg(detailedInfo, barreInfo);
    1315              : 
    1316            0 :     if (detailedInfo.trimmed().size() == 0) {
    1317            0 :         detailedInfo = muse::mtrc("engraving", "no content");
    1318              :     }
    1319              : 
    1320            0 :     String chordName = m_harmony
    1321            0 :                        ? muse::mtrc("engraving", "with chord symbol %1").arg(m_harmony->generateScreenReaderInfo())
    1322            0 :                        : muse::mtrc("engraving", "without chord symbol");
    1323              : 
    1324            0 :     String basicInfo = String(u"%1 %2").arg(translatedTypeUserName(), chordName);
    1325              : 
    1326            0 :     String generalInfo = muse::mtrc("engraving", "%n string(s) total", "", m_strings);
    1327              : 
    1328            0 :     String res = String(u"%1 %2 %3").arg(basicInfo, generalInfo, detailedInfo);
    1329              : 
    1330            0 :     return res;
    1331            0 : }
    1332              : 
    1333            0 : void FretDiagram::setFingering(std::vector<int> v)
    1334              : {
    1335            0 :     m_fingering = std::move(v);
    1336            0 : }
    1337              : 
    1338           22 : FretDiagram* FretDiagram::makeFromHarmonyOrFretDiagram(const EngravingItem* harmonyOrFretDiagram)
    1339              : {
    1340           22 :     IF_ASSERT_FAILED(!harmonyOrFretDiagram || !harmonyOrFretDiagram->isHarmony() || !harmonyOrFretDiagram->isFretDiagram()) {
    1341            0 :         return nullptr;
    1342              :     }
    1343              : 
    1344           22 :     FretDiagram* fretDiagram = nullptr;
    1345              : 
    1346           22 :     if (harmonyOrFretDiagram->isHarmony() && !harmonyOrFretDiagram->ownershipParent()->isFretDiagram()) {
    1347            0 :         Harmony* harmony = toHarmony(harmonyOrFretDiagram)->clone();
    1348              : 
    1349            0 :         fretDiagram = Factory::createFretDiagram(harmonyOrFretDiagram->score()->dummy());
    1350              : 
    1351            0 :         fretDiagram->updateDiagram(harmony->plainText());
    1352              : 
    1353            0 :         harmony->resetProperty(Pid::OFFSET);
    1354            0 :         harmony->setProperty(Pid::ALIGN, Align(AlignH::HCENTER, AlignV::BASELINE));
    1355            0 :         harmony->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
    1356              : 
    1357            0 :         fretDiagram->add(harmony);
    1358           22 :     } else if (harmonyOrFretDiagram->isHarmony() && harmonyOrFretDiagram->ownershipParent()->isFretDiagram()) {
    1359            0 :         fretDiagram = toFretDiagram(harmonyOrFretDiagram->ownershipParent())->clone();
    1360           22 :     } else if (harmonyOrFretDiagram->isFretDiagram()) {
    1361           22 :         fretDiagram = toFretDiagram(harmonyOrFretDiagram)->clone();
    1362           22 :         fretDiagram->setOffset(PointF());
    1363           22 :         if (!fretDiagram->harmony()) {
    1364              :             //! generate from diagram and add harmony
    1365            0 :             fretDiagram->add(Factory::createHarmony(fretDiagram));
    1366              :         }
    1367              :     }
    1368              : 
    1369           22 :     return fretDiagram;
    1370              : }
    1371              : 
    1372            0 : bool FretDiagram::isInFretBox() const
    1373              : {
    1374            0 :     EngravingObject* parent = ownershipParent();
    1375            0 :     return parent ? parent->isFBox() : false;
    1376              : }
    1377              : 
    1378            0 : bool FretDiagram::isCustom(const String& harmonyNameForCompare) const
    1379              : {
    1380            0 :     if (harmonyNameForCompare.empty()) {
    1381            0 :         return false;
    1382              :     }
    1383              : 
    1384              :     static constexpr std::array props {
    1385              :         Pid::FRET_STRINGS,
    1386              :         Pid::FRET_FRETS,
    1387              :         Pid::FRET_NUT,
    1388              :         Pid::FRET_FINGERING
    1389              :     };
    1390              : 
    1391            0 :     for (const Pid& pid : props) {
    1392            0 :         if (pid == Pid::FRET_FINGERING) {
    1393            0 :             if (custom(Pid::FRET_SHOW_FINGERINGS)) {
    1394            0 :                 return true;
    1395              :             }
    1396            0 :         } else if (custom(pid)) {
    1397            0 :             return true;
    1398              :         }
    1399              :     }
    1400              : 
    1401            0 :     if (s_harmonyToDiagramMap.empty()) {
    1402            0 :         readHarmonyToDiagramFile(HARMONY_TO_DIAGRAM_FILE_PATH);
    1403              :     }
    1404              : 
    1405            0 :     NoteSpellingType spellingType = style().styleV(Sid::chordSymbolSpelling).value<NoteSpellingType>();
    1406            0 :     HarmonyMapKey key = createHarmonyMapKey(harmonyNameForCompare, spellingType, score()->chordList());
    1407              : 
    1408            0 :     if (tpcIsValid(key.rootTpc)) {
    1409            0 :         key.rootTpc = canonicalEnharmonicTpc(key.rootTpc);
    1410              :     }
    1411            0 :     if (tpcIsValid(key.bassTpc)) {
    1412            0 :         key.bassTpc = canonicalEnharmonicTpc(key.bassTpc);
    1413              :     }
    1414              : 
    1415            0 :     std::vector<DiagramInfo> availableDiagrams = muse::value(s_harmonyToDiagramMap, key);
    1416            0 :     if (availableDiagrams.empty()) {
    1417            0 :         return true;
    1418              :     }
    1419              : 
    1420            0 :     String currentPattern = patternFromDiagram();
    1421              : 
    1422            0 :     for (const DiagramInfo& diagram : availableDiagrams) {
    1423            0 :         if (diagram.diagramPattern == currentPattern) {
    1424            0 :             return false;
    1425              :         }
    1426              :     }
    1427              : 
    1428            0 :     return true;
    1429            0 : }
    1430              : 
    1431            2 : void FretDiagram::readHarmonyToDiagramFile(const muse::io::path_t& filePath) const
    1432              : {
    1433            4 :     TRACEFUNC;
    1434              : 
    1435            2 :     muse::io::File file(filePath);
    1436            2 :     if (!file.open()) {
    1437            0 :         LOGE() << file.errorString();
    1438            0 :         return;
    1439              :     }
    1440              : 
    1441            2 :     XmlReader reader(&file);
    1442              : 
    1443              :     std::unordered_map<String, rw::HarmonyToDiagramReader::FretDiagramInfo> harmonyToDiagramMap
    1444            2 :         = rw::HarmonyToDiagramReader::read(reader);
    1445              : 
    1446            2 :     const ChordList* chordList = score()->chordList();
    1447            2 :     const NoteSpellingType spellingType = NoteSpellingType::STANDARD;
    1448              : 
    1449         6250 :     for (auto& [key, value] : harmonyToDiagramMap) {
    1450         6248 :         HarmonyMapKey mapKey = createHarmonyMapKey(key, spellingType, chordList);
    1451              : 
    1452         6248 :         if (tpcIsValid(mapKey.rootTpc)) {
    1453         6246 :             mapKey.rootTpc = canonicalEnharmonicTpc(mapKey.rootTpc);
    1454              :         }
    1455         6248 :         if (tpcIsValid(mapKey.bassTpc)) {
    1456         3512 :             mapKey.bassTpc = canonicalEnharmonicTpc(mapKey.bassTpc);
    1457              :         }
    1458              : 
    1459        12496 :         s_harmonyToDiagramMap[mapKey].push_back({ key, value.xml, value.pattern });
    1460         6248 :         s_diagramPatternToHarmoniesMap[value.pattern].push_back(key);
    1461              :     }
    1462         6250 : }
    1463              : 
    1464              : //---------------------------------------------------------
    1465              : //   markerToChar
    1466              : //---------------------------------------------------------
    1467              : 
    1468            0 : Char FretItem::markerToChar(FretMarkerType t)
    1469              : {
    1470            0 :     switch (t) {
    1471            0 :     case FretMarkerType::CIRCLE: return Char(u'O');
    1472            0 :     case FretMarkerType::CROSS: return Char(u'X');
    1473            0 :     case FretMarkerType::NONE:
    1474              :     default:
    1475            0 :         return Char();
    1476              :     }
    1477              : }
    1478              : 
    1479              : //---------------------------------------------------------
    1480              : //   markerTypeToName
    1481              : //---------------------------------------------------------
    1482              : 
    1483              : const std::vector<FretItem::MarkerTypeNameItem> FretItem::markerTypeNameMap = {
    1484              :     { FretMarkerType::CIRCLE,     "circle" },
    1485              :     { FretMarkerType::CROSS,      "cross" },
    1486              :     { FretMarkerType::NONE,       "none" }
    1487              : };
    1488              : 
    1489          130 : String FretItem::markerTypeToName(FretMarkerType t)
    1490              : {
    1491          177 :     for (auto i : FretItem::markerTypeNameMap) {
    1492          177 :         if (i.mtype == t) {
    1493          130 :             return String::fromAscii(i.name);
    1494              :         }
    1495              :     }
    1496              : 
    1497            0 :     ASSERT_X("Unrecognised FretMarkerType!");
    1498              :     return String();         // prevent compiler warnings
    1499              : }
    1500              : 
    1501              : //---------------------------------------------------------
    1502              : //   nameToMarkerType
    1503              : //---------------------------------------------------------
    1504              : 
    1505          271 : FretMarkerType FretItem::nameToMarkerType(String n)
    1506              : {
    1507          366 :     for (auto i : FretItem::markerTypeNameMap) {
    1508          366 :         if (String::fromAscii(i.name) == n) {
    1509          271 :             return i.mtype;
    1510              :         }
    1511              :     }
    1512            0 :     LOGW("Unrecognised marker name!");
    1513            0 :     return FretMarkerType::NONE;         // default
    1514              : }
    1515              : 
    1516              : //---------------------------------------------------------
    1517              : //   dotTypeToName
    1518              : //---------------------------------------------------------
    1519              : 
    1520              : const std::vector<FretItem::DotTypeNameItem> FretItem::dotTypeNameMap = {
    1521              :     { FretDotType::NORMAL,        "normal" },
    1522              :     { FretDotType::CROSS,         "cross" },
    1523              :     { FretDotType::SQUARE,        "square" },
    1524              :     { FretDotType::TRIANGLE,      "triangle" },
    1525              : };
    1526              : 
    1527          202 : String FretItem::dotTypeToName(FretDotType t)
    1528              : {
    1529          202 :     for (auto i : FretItem::dotTypeNameMap) {
    1530          202 :         if (i.dtype == t) {
    1531          202 :             return String::fromAscii(i.name);
    1532              :         }
    1533              :     }
    1534              : 
    1535            0 :     ASSERT_X("Unrecognised FretDotType!");
    1536              :     return String();         // prevent compiler warnings
    1537              : }
    1538              : 
    1539              : //---------------------------------------------------------
    1540              : //   nameToDotType
    1541              : //---------------------------------------------------------
    1542              : 
    1543          333 : FretDotType FretItem::nameToDotType(String n)
    1544              : {
    1545          333 :     for (auto i : FretItem::dotTypeNameMap) {
    1546          333 :         if (String::fromAscii(i.name) == n) {
    1547          333 :             return i.dtype;
    1548              :         }
    1549              :     }
    1550            0 :     LOGW("Unrecognised dot name!");
    1551            0 :     return FretDotType::NORMAL;         // default
    1552              : }
    1553              : }
        

Generated by: LCOV version 2.0-1