LCOV - code coverage report
Current view: top level - src/engraving/dom - articulation.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 57.6 % 427 246
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 "articulation.h"
      24              : 
      25              : #include "style/style.h"
      26              : #include "types/symnames.h"
      27              : #include "types/typesconv.h"
      28              : #include "types/translatablestring.h"
      29              : 
      30              : #include "beam.h"
      31              : #include "chord.h"
      32              : #include "chordrest.h"
      33              : #include "measure.h"
      34              : #include "page.h"
      35              : #include "staff.h"
      36              : #include "stafftype.h"
      37              : #include "system.h"
      38              : #include "note.h"
      39              : 
      40              : #include "log.h"
      41              : 
      42              : using namespace mu;
      43              : using namespace mu::engraving;
      44              : using namespace muse::draw;
      45              : 
      46              : //---------------------------------------------------------
      47              : //   articulationStyle
      48              : //---------------------------------------------------------
      49              : 
      50              : static const ElementStyle articulationStyle {
      51              :     { Sid::articulationMinDistance, Pid::MIN_DISTANCE },
      52              :     { Sid::articulationAnchorDefault, Pid::ARTICULATION_ANCHOR },
      53              : };
      54              : 
      55              : //---------------------------------------------------------
      56              : //   Articulation
      57              : //---------------------------------------------------------
      58              : 
      59         1328 : Articulation::Articulation(DummyParentOr<ChordRest> parent, ElementType type)
      60         1328 :     : EngravingItem(type, parent, ElementFlag::MOVABLE)
      61              : {
      62         1328 :     m_symId         = SymId::noSym;
      63         1328 :     m_anchor        = ArticulationAnchor::AUTO;
      64         1328 :     m_direction     = DirectionV::AUTO;
      65         1328 :     m_ornamentStyle = OrnamentStyle::DEFAULT;
      66         1328 :     m_playArticulation = true;
      67              : 
      68         1328 :     initElementStyle(&articulationStyle);
      69         1328 :     setupShowOnTabStyles();
      70         1328 : }
      71              : 
      72              : //---------------------------------------------------------
      73              : //   setSymId
      74              : //---------------------------------------------------------
      75              : 
      76         1315 : void Articulation::setSymId(SymId id)
      77              : {
      78         1315 :     m_symId  = id;
      79         1315 :     computeCategories();
      80         1315 :     setupShowOnTabStyles();
      81         1315 :     m_anchor = ArticulationAnchor(propertyDefault(Pid::ARTICULATION_ANCHOR).toInt());
      82         1315 :     m_textType = ArticulationTextType::NO_TEXT;
      83         1315 : }
      84              : 
      85              : //---------------------------------------------------------
      86              : //   setTextType
      87              : //---------------------------------------------------------
      88              : 
      89           28 : void Articulation::setTextType(ArticulationTextType textType)
      90              : {
      91           28 :     m_textType = textType;
      92           28 : }
      93              : 
      94          647 : void Articulation::setSelected(bool f)
      95              : {
      96          647 :     if (m_text) {
      97            0 :         m_text->setSelected(f);
      98              :     }
      99              : 
     100          647 :     EngravingItem::setSelected(f);
     101          647 : }
     102              : 
     103          344 : void Articulation::setVisible(bool f)
     104              : {
     105          344 :     if (m_text) {
     106            0 :         m_text->setVisible(f);
     107              :     }
     108              : 
     109          344 :     EngravingItem::setVisible(f);
     110          344 : }
     111              : 
     112              : //---------------------------------------------------------
     113              : //   subtype
     114              : //---------------------------------------------------------
     115              : 
     116         2123 : int Articulation::subtype() const
     117              : {
     118         2123 :     if (m_textType != ArticulationTextType::NO_TEXT) {
     119           16 :         return int(m_textType);
     120              :     }
     121         2107 :     String s = String::fromAscii(SymNames::nameForSymId(m_symId).ascii());
     122         2107 :     if (s.endsWith(u"Below")) {
     123          589 :         return int(SymNames::symIdByName(s.left(s.size() - 5) + u"Above"));
     124         1518 :     } else if (s.endsWith(u"Turned")) {
     125           24 :         return int(SymNames::symIdByName(s.left(s.size() - 6)));
     126              :     }
     127              : 
     128         1494 :     return int(m_symId);
     129         2107 : }
     130              : 
     131              : //---------------------------------------------------------
     132              : //   setUp
     133              : //---------------------------------------------------------
     134              : 
     135         3469 : void Articulation::setUp(bool val)
     136              : {
     137         3469 :     Articulation::LayoutData* ldata = mutldata();
     138         3469 :     ldata->up = val;
     139              : 
     140              :     //! NOTE member of Articulation m_symId - this is `given` data
     141              :     //! member of LayoutData m_symId - this is layout data
     142              :     //! I would not like to change the `given` data here, but they are changing for backward compatibility
     143              :     //! Even better, I wouldn’t want symId to be `given` data,
     144              :     //! it would be better if there was some type,
     145              :     //! and from it we would already figure out how (with what symbol) to display it
     146              : 
     147         3469 :     bool dup = m_direction == DirectionV::AUTO ? val : m_direction == DirectionV::UP;
     148         3469 :     String s = String::fromAscii(SymNames::nameForSymId(m_symId).ascii());
     149         3469 :     if (s.endsWith(!dup ? u"Above" : u"Below")) {
     150          179 :         String s2 = s.left(s.size() - 5) + (dup ? u"Above" : u"Below");
     151          179 :         m_symId = SymNames::symIdByName(s2);
     152         3469 :     } else if (s.endsWith(u"Turned")) {
     153           28 :         String s2 = dup ? s.left(s.size() - 6) : s;
     154           28 :         m_symId = SymNames::symIdByName(s2);
     155         3290 :     } else if (!dup) {
     156          867 :         String s2 = s + u"Turned";
     157          867 :         SymId sym = SymNames::symIdByName(s2);
     158          867 :         if (sym != SymId::noSym) {
     159           12 :             m_symId = sym;
     160              :         }
     161          867 :     }
     162              : 
     163         3469 :     ldata->symId = m_symId;
     164         3469 : }
     165              : 
     166              : //---------------------------------------------------------
     167              : //   typeUserName
     168              : //---------------------------------------------------------
     169              : 
     170            0 : muse::TranslatableString Articulation::typeUserName() const
     171              : {
     172            0 :     if (m_textType != ArticulationTextType::NO_TEXT) {
     173            0 :         return TranslatableString("engraving", "Articulation text");
     174              :     }
     175              : 
     176            0 :     return TranslatableString("engraving", "Articulation");
     177              : }
     178              : 
     179              : //---------------------------------------------------------
     180              : //   subtypeUserName
     181              : //---------------------------------------------------------
     182              : 
     183            2 : muse::TranslatableString Articulation::subtypeUserName() const
     184              : {
     185            2 :     if (m_textType != ArticulationTextType::NO_TEXT) {
     186            0 :         return TConv::userName(m_textType);
     187              :     }
     188              : 
     189            2 :     return SymNames::userNameForSymId(symId());
     190              : }
     191              : 
     192              : //---------------------------------------------------------
     193              : //   chordRest
     194              : //---------------------------------------------------------
     195              : 
     196        17861 : ChordRest* Articulation::chordRest() const
     197              : {
     198        17861 :     if (ownershipParent() && ownershipParent()->isChordRest()) {
     199        17861 :         return toChordRest(ownershipParent());
     200              :     }
     201            0 :     return 0;
     202              : }
     203              : 
     204         4743 : Segment* Articulation::segment() const
     205              : {
     206         4743 :     ChordRest* cr = chordRest();
     207         4743 :     if (!cr) {
     208            0 :         return 0;
     209              :     }
     210              : 
     211         4743 :     Segment* s = 0;
     212         4743 :     if (cr->isGrace()) {
     213            2 :         if (cr->ownershipParent()) {
     214            2 :             s = toSegment(cr->ownershipParent()->ownershipParent());
     215              :         }
     216              :     } else {
     217         4741 :         s = toSegment(cr->ownershipParent());
     218              :     }
     219              : 
     220         4743 :     return s;
     221              : }
     222              : 
     223         1339 : Measure* Articulation::measure() const
     224              : {
     225         1339 :     Segment* s = segment();
     226         1339 :     return toMeasure(s ? s->ownershipParent() : 0);
     227              : }
     228              : 
     229            0 : System* Articulation::system() const
     230              : {
     231            0 :     Measure* m = measure();
     232            0 :     return m ? m->system() : nullptr;
     233              : }
     234              : 
     235            0 : Page* Articulation::page() const
     236              : {
     237            0 :     System* s = system();
     238            0 :     return s ? s->page() : nullptr;
     239              : }
     240              : 
     241         3435 : bool Articulation::isHiddenOnTabStaff() const
     242              : {
     243         3435 :     if (m_showOnTabStyles.first == Sid::NOSTYLE || m_showOnTabStyles.second == Sid::NOSTYLE) {
     244         1497 :         return false;
     245              :     }
     246              : 
     247         1938 :     const StaffType* stType = staffType();
     248              : 
     249         1938 :     if (!stType || !stType->isTabStaff()) {
     250         1924 :         return false;
     251              :     }
     252              : 
     253           14 :     return stType->isHiddenElementOnTab(m_showOnTabStyles.first, m_showOnTabStyles.second);
     254              : }
     255              : 
     256              : //---------------------------------------------------------
     257              : //   layoutCloseToNote
     258              : //    Needed to figure out the layout policy regarding
     259              : //    distance to the note and placement in relation to
     260              : //    slur.
     261              : //---------------------------------------------------------
     262              : 
     263        22205 : bool Articulation::layoutCloseToNote() const
     264              : {
     265        22205 :     Staff* s = staff();
     266        22205 :     if (s && s->staffType()->isTabStaff() && isStaccato()) {
     267           84 :         return false;
     268              :     }
     269              : 
     270        22121 :     return (isStaccato() || isTenuto()) && !isDouble();
     271              : }
     272              : 
     273              : //---------------------------------------------------------
     274              : //   dragAnchorLines
     275              : //---------------------------------------------------------
     276              : 
     277            0 : std::vector<LineF> Articulation::dragAnchorLines() const
     278              : {
     279            0 :     std::vector<LineF> result;
     280            0 :     result.push_back(LineF(canvasPos(), layoutParent()->canvasPos()));
     281            0 :     return result;
     282            0 : }
     283              : 
     284              : //---------------------------------------------------------
     285              : //   getProperty
     286              : //---------------------------------------------------------
     287              : 
     288         9289 : PropertyValue Articulation::getProperty(Pid propertyId) const
     289              : {
     290         9289 :     switch (propertyId) {
     291            0 :     case Pid::SYMBOL:              return PropertyValue::fromValue(m_symId);
     292          761 :     case Pid::DIRECTION:           return PropertyValue::fromValue<DirectionV>(direction());
     293           92 :     case Pid::ARTICULATION_ANCHOR: return int(anchor());
     294          761 :     case Pid::ORNAMENT_STYLE:      return ornamentStyle();
     295          761 :     case Pid::PLAY:                return playArticulation();
     296         6914 :     default:
     297         6914 :         return EngravingItem::getProperty(propertyId);
     298              :     }
     299              : }
     300              : 
     301              : //---------------------------------------------------------
     302              : //   setProperty
     303              : //---------------------------------------------------------
     304              : 
     305         3241 : bool Articulation::setProperty(Pid propertyId, const PropertyValue& v)
     306              : {
     307         3241 :     switch (propertyId) {
     308            0 :     case Pid::SYMBOL:
     309            0 :         setSymId(v.value<SymId>());
     310            0 :         break;
     311            0 :     case Pid::DIRECTION:
     312            0 :         setDirection(v.value<DirectionV>());
     313            0 :         break;
     314         1422 :     case Pid::ARTICULATION_ANCHOR:
     315         1422 :         setAnchor(ArticulationAnchor(v.toInt()));
     316         1422 :         break;
     317            0 :     case Pid::PLAY:
     318            0 :         setPlayArticulation(v.toBool());
     319            0 :         break;
     320            5 :     case Pid::ORNAMENT_STYLE:
     321            5 :         setOrnamentStyle(v.value<OrnamentStyle>());
     322            5 :         break;
     323         1814 :     default:
     324         1814 :         return EngravingItem::setProperty(propertyId, v);
     325              :     }
     326         1427 :     triggerLayout();
     327         1427 :     return true;
     328              : }
     329              : 
     330              : //---------------------------------------------------------
     331              : //   propertyDefault
     332              : //---------------------------------------------------------
     333              : 
     334        10232 : PropertyValue Articulation::propertyDefault(Pid propertyId) const
     335              : {
     336        10232 :     switch (propertyId) {
     337          761 :     case Pid::DIRECTION:
     338          761 :         return DirectionV::AUTO;
     339              : 
     340          761 :     case Pid::ORNAMENT_STYLE:
     341          761 :         return OrnamentStyle::DEFAULT;
     342              : 
     343          761 :     case Pid::PLAY:
     344          761 :         return true;
     345              : 
     346         7949 :     default:
     347         7949 :         break;
     348              :     }
     349         7949 :     return EngravingItem::propertyDefault(propertyId);
     350              : }
     351              : 
     352              : //---------------------------------------------------------
     353              : //   anchorGroup
     354              : //---------------------------------------------------------
     355              : 
     356         2473 : Articulation::AnchorGroup Articulation::anchorGroup(SymId symId)
     357              : {
     358         2473 :     switch (symId) {
     359          852 :     case SymId::articAccentAbove:
     360              :     case SymId::articAccentBelow:
     361              :     case SymId::articStaccatoAbove:
     362              :     case SymId::articStaccatoBelow:
     363              :     case SymId::articStaccatissimoAbove:
     364              :     case SymId::articStaccatissimoBelow:
     365              :     case SymId::articTenutoAbove:
     366              :     case SymId::articTenutoBelow:
     367              :     case SymId::articTenutoStaccatoAbove:
     368              :     case SymId::articTenutoStaccatoBelow:
     369              :     case SymId::articMarcatoAbove:
     370              :     case SymId::articMarcatoBelow:
     371              : 
     372              :     case SymId::articAccentStaccatoAbove:
     373              :     case SymId::articAccentStaccatoBelow:
     374              :     case SymId::articLaissezVibrerAbove:
     375              :     case SymId::articLaissezVibrerBelow:
     376              :     case SymId::articMarcatoStaccatoAbove:
     377              :     case SymId::articMarcatoStaccatoBelow:
     378              :     case SymId::articMarcatoTenutoAbove:
     379              :     case SymId::articMarcatoTenutoBelow:
     380              :     case SymId::articStaccatissimoStrokeAbove:
     381              :     case SymId::articStaccatissimoStrokeBelow:
     382              :     case SymId::articStaccatissimoWedgeAbove:
     383              :     case SymId::articStaccatissimoWedgeBelow:
     384              :     case SymId::articStressAbove:
     385              :     case SymId::articStressBelow:
     386              :     case SymId::articTenutoAccentAbove:
     387              :     case SymId::articTenutoAccentBelow:
     388              :     case SymId::articUnstressAbove:
     389              :     case SymId::articUnstressBelow:
     390              : 
     391              :     case SymId::articSoftAccentAbove:
     392              :     case SymId::articSoftAccentBelow:
     393              :     case SymId::articSoftAccentStaccatoAbove:
     394              :     case SymId::articSoftAccentStaccatoBelow:
     395              :     case SymId::articSoftAccentTenutoAbove:
     396              :     case SymId::articSoftAccentTenutoBelow:
     397              :     case SymId::articSoftAccentTenutoStaccatoAbove:
     398              :     case SymId::articSoftAccentTenutoStaccatoBelow:
     399              : 
     400              :     case SymId::wiggleSawtooth:
     401              :     case SymId::wiggleSawtoothWide:
     402              :     case SymId::wiggleVibratoLargeFaster:
     403              :     case SymId::wiggleVibratoLargeSlowest:
     404          852 :         return AnchorGroup::ARTICULATION;
     405              : 
     406            0 :     case SymId::luteFingeringRHThumb:
     407              :     case SymId::luteFingeringRHFirst:
     408              :     case SymId::luteFingeringRHSecond:
     409              :     case SymId::luteFingeringRHThird:
     410            0 :         return AnchorGroup::LUTE_FINGERING;
     411              : 
     412            0 :     case SymId::tremoloDivisiDots2:
     413              :     case SymId::tremoloDivisiDots3:
     414              :     case SymId::tremoloDivisiDots4:
     415              :     case SymId::tremoloDivisiDots6:
     416            0 :         return AnchorGroup::ARTICULATION;
     417              : 
     418         1621 :     default:
     419         1621 :         break;
     420              :     }
     421         1621 :     return AnchorGroup::OTHER;
     422              : }
     423              : 
     424              : //---------------------------------------------------------
     425              : //   symId2ArticulationName
     426              : //---------------------------------------------------------
     427              : 
     428            3 : String Articulation::symId2ArticulationName(SymId symId)
     429              : {
     430            3 :     switch (symId) {
     431            0 :     case SymId::articStaccatissimoAbove:
     432              :     case SymId::articStaccatissimoBelow:
     433              :     case SymId::articStaccatissimoStrokeAbove:
     434              :     case SymId::articStaccatissimoStrokeBelow:
     435              :     case SymId::articStaccatissimoWedgeAbove:
     436              :     case SymId::articStaccatissimoWedgeBelow:
     437            0 :         return u"staccatissimo";
     438              : 
     439            3 :     case SymId::articStaccatoAbove:
     440              :     case SymId::articStaccatoBelow:
     441              :     case SymId::tremoloDivisiDots2:
     442              :     case SymId::tremoloDivisiDots3:
     443              :     case SymId::tremoloDivisiDots4:
     444              :     case SymId::tremoloDivisiDots6:
     445            3 :         return u"staccato";
     446              : 
     447            0 :     case SymId::articAccentStaccatoAbove:
     448              :     case SymId::articAccentStaccatoBelow:
     449            0 :         return u"sforzatoStaccato";
     450              : 
     451            0 :     case SymId::articMarcatoStaccatoAbove:
     452              :     case SymId::articMarcatoStaccatoBelow:
     453            0 :         return u"marcatoStaccato";
     454              : 
     455            0 :     case SymId::articTenutoStaccatoAbove:
     456              :     case SymId::articTenutoStaccatoBelow:
     457            0 :         return u"portato";
     458              : 
     459            0 :     case SymId::articTenutoAccentAbove:
     460              :     case SymId::articTenutoAccentBelow:
     461            0 :         return u"sforzatoTenuto";
     462              : 
     463            0 :     case SymId::articMarcatoTenutoAbove:
     464              :     case SymId::articMarcatoTenutoBelow:
     465            0 :         return u"marcatoTenuto";
     466              : 
     467            0 :     case SymId::articTenutoAbove:
     468              :     case SymId::articTenutoBelow:
     469            0 :         return u"tenuto";
     470              : 
     471            0 :     case SymId::articMarcatoAbove:
     472              :     case SymId::articMarcatoBelow:
     473            0 :         return u"marcato";
     474              : 
     475            0 :     case SymId::articAccentAbove:
     476              :     case SymId::articAccentBelow:
     477            0 :         return u"accent";
     478              : 
     479            0 :     case SymId::dynamicSforzato:
     480              :     case SymId::dynamicSforzando:
     481            0 :         return u"sforzato";
     482              : 
     483            0 :     case SymId::brassMuteOpen:
     484            0 :         return u"open";
     485              : 
     486            0 :     case SymId::brassMuteClosed:
     487            0 :         return u"closed";
     488              : 
     489            0 :     case SymId::stringsHarmonic:
     490            0 :         return u"harmonic";
     491              : 
     492            0 :     case SymId::ornamentMordent:
     493            0 :         return u"mordent";
     494              : 
     495            0 :     default:
     496            0 :         return u"---";
     497              :     }
     498              : }
     499              : 
     500              : //---------------------------------------------------------
     501              : //   articulationName
     502              : //---------------------------------------------------------
     503              : 
     504            3 : String Articulation::articulationName() const
     505              : {
     506            3 :     return symId2ArticulationName(m_symId);
     507              : }
     508              : 
     509              : //---------------------------------------------------------
     510              : //   getPropertyStyle
     511              : //---------------------------------------------------------
     512              : 
     513         4644 : Sid Articulation::getPropertyStyle(Pid id) const
     514              : {
     515         4644 :     switch (id) {
     516         1393 :     case Pid::MIN_DISTANCE:
     517         1393 :         return EngravingItem::getPropertyStyle(id);
     518              : 
     519         2490 :     case Pid::ARTICULATION_ANCHOR: {
     520         2490 :         if (isHandbellsArticulation()) {
     521           17 :             return Sid::articulationAnchorDefault;
     522              :         }
     523              : 
     524         2473 :         switch (anchorGroup(m_symId)) {
     525          852 :         case AnchorGroup::ARTICULATION:
     526          852 :             return Sid::articulationAnchorDefault;
     527            0 :         case AnchorGroup::LUTE_FINGERING:
     528            0 :             return Sid::articulationAnchorLuteFingering;
     529         1621 :         case AnchorGroup::OTHER:
     530         1621 :             return Sid::articulationAnchorOther;
     531              :         }
     532              :     }
     533            0 :         assert(false);           // should never be reached
     534              :     // fallthrough
     535          761 :     default:
     536          761 :         return Sid::NOSTYLE;
     537              :     }
     538              : }
     539              : 
     540              : //---------------------------------------------------------
     541              : //   resetProperty
     542              : //---------------------------------------------------------
     543              : 
     544            0 : void Articulation::resetProperty(Pid id)
     545              : {
     546            0 :     switch (id) {
     547            0 :     case Pid::DIRECTION:
     548              :     case Pid::ORNAMENT_STYLE:
     549            0 :         setProperty(id, propertyDefault(id));
     550            0 :         return;
     551            0 :     case Pid::ARTICULATION_ANCHOR:
     552            0 :         setProperty(id, propertyDefault(id));
     553            0 :         return;
     554              : 
     555            0 :     default:
     556            0 :         break;
     557              :     }
     558            0 :     EngravingItem::resetProperty(id);
     559              : }
     560              : 
     561              : //---------------------------------------------------------
     562              : //   mag
     563              : //---------------------------------------------------------
     564              : 
     565        65484 : double Articulation::mag() const
     566              : {
     567        65484 :     return layoutParent() ? layoutParent()->mag() * style().styleD(Sid::articulationMag) : 1.0;
     568              : }
     569              : 
     570         1315 : void Articulation::computeCategories()
     571              : {
     572         1315 :     m_categories.setFlag(ArticulationCategory::DOUBLE,
     573         1307 :                          m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
     574         1307 :                          || m_symId == SymId::articTenutoStaccatoAbove || m_symId == SymId::articTenutoStaccatoBelow
     575         1276 :                          || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow
     576         1267 :                          || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow
     577         2622 :                          || m_symId == SymId::articTenutoAccentAbove || m_symId == SymId::articTenutoAccentBelow);
     578              : 
     579         1315 :     m_categories.setFlag(ArticulationCategory::TENUTO,
     580         1220 :                          m_symId == SymId::articTenutoAbove || m_symId == SymId::articTenutoBelow
     581         1190 :                          || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow
     582         2535 :                          || m_symId == SymId::articTenutoAccentAbove || m_symId == SymId::articTenutoAccentBelow);
     583              : 
     584         1315 :     m_categories.setFlag(ArticulationCategory::STACCATO,
     585         1069 :                          m_symId == SymId::articStaccatoAbove || m_symId == SymId::articStaccatoBelow
     586         1015 :                          || m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
     587         1007 :                          || m_symId == SymId::articTenutoStaccatoAbove || m_symId == SymId::articTenutoStaccatoBelow
     588          976 :                          || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow
     589          967 :                          || m_symId == SymId::tremoloDivisiDots2 || m_symId == SymId::tremoloDivisiDots3
     590         2384 :                          || m_symId == SymId::tremoloDivisiDots4 || m_symId == SymId::tremoloDivisiDots6);
     591              : 
     592         1315 :     m_categories.setFlag(ArticulationCategory::ACCENT,
     593         1226 :                          m_symId == SymId::articAccentAbove || m_symId == SymId::articAccentBelow
     594         2541 :                          || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow);
     595              : 
     596         1315 :     m_categories.setFlag(ArticulationCategory::MARCATO,
     597         1258 :                          m_symId == SymId::articMarcatoAbove || m_symId == SymId::articMarcatoBelow
     598         1257 :                          || m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
     599         2573 :                          || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow);
     600              : 
     601         1315 :     m_categories.setFlag(ArticulationCategory::LUTE_FINGERING,
     602         1312 :                          m_symId == SymId::stringsThumbPosition || m_symId == SymId::luteFingeringRHThumb
     603         1312 :                          || m_symId == SymId::luteFingeringRHFirst || m_symId == SymId::luteFingeringRHSecond
     604         2627 :                          || m_symId == SymId::luteFingeringRHThird);
     605         1315 :     m_categories.setFlag(ArticulationCategory::LAISSEZ_VIB,
     606         1315 :                          m_symId == SymId::articLaissezVibrerAbove || m_symId == SymId::articLaissezVibrerBelow);
     607              : 
     608         1315 :     m_categories.setFlag(ArticulationCategory::HANDBELLS,
     609         1315 :                          (static_cast<int>(m_symId) >= static_cast<int>(SymId::handbellsBelltree)
     610          505 :                           && static_cast<int>(m_symId) <= static_cast<int>(SymId::handbellsTableSingleBell))
     611         1820 :                          || (static_cast<int>(m_textType) >= static_cast<int>(ArticulationTextType::TD)
     612            0 :                              && static_cast<int>(m_textType) <= static_cast<int>(ArticulationTextType::VIB))
     613              :                          );
     614         1315 : }
     615              : 
     616         1823 : bool Articulation::isBasicArticulation() const
     617              : {
     618              :     static const std::set<SymId> articulations{
     619              :         SymId::articAccentAbove, SymId::articAccentBelow,
     620              :         SymId::articStaccatoAbove, SymId::articStaccatoBelow,
     621              :         SymId::articTenutoAbove, SymId::articTenutoBelow,
     622              :         SymId::articMarcatoAbove, SymId::articMarcatoBelow,
     623              : 
     624              :         SymId::articAccentStaccatoAbove, SymId::articAccentStaccatoBelow,
     625              :         SymId::articMarcatoStaccatoAbove, SymId::articMarcatoStaccatoBelow,
     626              :         SymId::articMarcatoTenutoAbove, SymId::articMarcatoTenutoBelow,
     627              :         SymId::articTenutoStaccatoAbove, SymId::articTenutoStaccatoBelow,
     628              :         SymId::articTenutoAccentAbove, SymId::articTenutoAccentBelow,
     629              :         SymId::articStaccatissimoAbove, SymId::articStaccatissimoBelow,
     630              :         SymId::articStaccatissimoStrokeAbove, SymId::articStaccatissimoStrokeBelow,
     631              :         SymId::articStaccatissimoWedgeAbove, SymId::articStaccatissimoWedgeBelow,
     632              :         SymId::articStressAbove, SymId::articStressBelow
     633         1839 :     };
     634         1823 :     SymId symId = static_cast<SymId>(subtype());
     635         1823 :     return articulations.find(symId) != articulations.end();
     636              : }
     637              : 
     638              : //---------------------------------------------------------
     639              : //   accessibleInfo
     640              : //---------------------------------------------------------
     641              : 
     642            0 : String Articulation::accessibleInfo() const
     643              : {
     644            0 :     return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), translatedSubtypeUserName());
     645              : }
     646              : 
     647         2643 : void Articulation::setupShowOnTabStyles()
     648              : {
     649              :     /// staccato
     650         2643 :     if (isStaccato()) {
     651          348 :         m_showOnTabStyles = { Sid::staccatoShowTabCommon, Sid::staccatoShowTabSimple };
     652              :     }
     653              : 
     654              :     /// accent
     655         2643 :     if (isAccent() || isMarcato()) {
     656          236 :         m_showOnTabStyles = { Sid::accentShowTabCommon, Sid::accentShowTabSimple };
     657              :     }
     658              : 
     659              :     /// turn
     660         2643 :     if (m_symId == SymId::ornamentTurn || m_symId == SymId::ornamentTurnInverted) {
     661           65 :         m_showOnTabStyles = { Sid::turnShowTabCommon, Sid::turnShowTabSimple };
     662              :     }
     663              : 
     664              :     /// mordent
     665         2643 :     if (m_symId == SymId::ornamentMordent || m_symId == SymId::ornamentShortTrill) {
     666           63 :         m_showOnTabStyles = { Sid::mordentShowTabCommon, Sid::mordentShowTabSimple };
     667              :     }
     668              : 
     669              :     /// wah
     670         2643 :     if (m_symId == SymId::brassMuteOpen || m_symId == SymId::brassMuteClosed) {
     671           25 :         m_showOnTabStyles = { Sid::wahShowTabCommon, Sid::wahShowTabSimple };
     672              :     }
     673              : 
     674              :     /// golpe
     675         2643 :     if (m_symId == SymId::guitarGolpe) {
     676            0 :         m_showOnTabStyles = { Sid::golpeShowTabCommon, Sid::golpeShowTabSimple };
     677              :     }
     678         2643 : }
     679              : 
     680           65 : void Articulation::styleChanged()
     681              : {
     682           65 :     if (m_text) {
     683            0 :         m_text->styleChanged();
     684              :     }
     685              : 
     686           65 :     bool isGolpeThumb = m_symId == SymId::guitarGolpe && m_anchor == ArticulationAnchor::BOTTOM;
     687           65 :     EngravingItem::styleChanged();
     688           65 :     if (isGolpeThumb) {
     689            0 :         setAnchor(ArticulationAnchor::BOTTOM);
     690              :     }
     691           65 : }
     692              : 
     693         3454 : bool Articulation::isOnCrossBeamSide() const
     694              : {
     695         3454 :     ChordRest* cr = chordRest();
     696         3454 :     if (!cr || !cr->isChord()) {
     697            0 :         return false;
     698              :     }
     699         3454 :     Chord* chord = toChord(cr);
     700         3454 :     return chord->beam() && (chord->beam()->cross() || chord->staffMove() != 0) && (up() == chord->up());
     701              : }
     702              : 
     703         7192 : staff_idx_t Articulation::vStaffIdx() const
     704              : {
     705         7192 :     ChordRest* cr = chordRest();
     706         7192 :     if (!cr) {
     707            0 :         return staffIdx();
     708              :     }
     709         7192 :     return cr->vStaffIdx();
     710              : }
     711              : 
     712              : struct ArticulationGroup
     713              : {
     714              :     SymId first;
     715              :     SymId second;
     716              : 
     717          266 :     bool operator <(const ArticulationGroup& other) const
     718              :     {
     719          266 :         if ((first == other.first && second == other.second)
     720          266 :             || (first == other.second && second == other.first)) {
     721            0 :             return false;
     722              :         }
     723              : 
     724          266 :         if (first != other.first) {
     725           76 :             return first < other.first;
     726              :         }
     727              : 
     728          190 :         return second < other.second;
     729              :     }
     730              : };
     731              : 
     732              : namespace mu::engraving {
     733              : static const std::map<SymId, ArticulationGroup> ARTICULATION_ABOVE_SPLIT_GROUPS = {
     734              :     { SymId::articAccentStaccatoAbove, { SymId::articStaccatoAbove, SymId::articAccentAbove } },
     735              :     { SymId::articTenutoAccentAbove, { SymId::articTenutoAbove, SymId::articAccentAbove } },
     736              :     { SymId::articTenutoStaccatoAbove, { SymId::articTenutoAbove, SymId::articStaccatoAbove } },
     737              :     { SymId::articMarcatoStaccatoAbove, { SymId::articStaccatoAbove, SymId::articMarcatoAbove } },
     738              :     { SymId::articMarcatoTenutoAbove, { SymId::articTenutoAbove, SymId::articMarcatoAbove } },
     739              : };
     740              : 
     741              : static const std::map<SymId, ArticulationGroup> ARTICULATION_BELOW_SPLIT_GROUP = {
     742              :     { SymId::articAccentStaccatoBelow, { SymId::articStaccatoBelow, SymId::articAccentBelow } },
     743              :     { SymId::articTenutoAccentBelow, { SymId::articTenutoBelow, SymId::articAccentBelow } },
     744              :     { SymId::articTenutoStaccatoBelow, { SymId::articTenutoBelow, SymId::articStaccatoBelow } },
     745              :     { SymId::articMarcatoStaccatoBelow, { SymId::articStaccatoBelow, SymId::articMarcatoBelow } },
     746              :     { SymId::articMarcatoTenutoBelow, { SymId::articTenutoBelow, SymId::articMarcatoBelow } },
     747              : };
     748              : 
     749              : static const std::map<ArticulationGroup, SymId> ARTICULATION_ABOVE_JOIN_GROUPS = {
     750              :     { { SymId::articStaccatoAbove, SymId::articAccentAbove }, SymId::articAccentStaccatoAbove },
     751              :     { { SymId::articTenutoAbove, SymId::articAccentAbove }, SymId::articTenutoAccentAbove },
     752              :     { { SymId::articTenutoAbove, SymId::articStaccatoAbove }, SymId::articTenutoStaccatoAbove },
     753              :     { { SymId::articStaccatoAbove, SymId::articMarcatoAbove }, SymId::articMarcatoStaccatoAbove },
     754              :     { { SymId::articTenutoAbove, SymId::articMarcatoAbove }, SymId::articMarcatoTenutoAbove },
     755              : };
     756              : 
     757              : static const std::map<SymId, SymId> ARTICULATION_PLACEMENTS = {
     758              :     { SymId::articStaccatissimoAbove, SymId::articStaccatissimoBelow },
     759              :     { SymId::articStaccatissimoStrokeAbove, SymId::articStaccatissimoStrokeBelow },
     760              :     { SymId::articStaccatissimoWedgeAbove, SymId::articStaccatissimoWedgeBelow },
     761              :     { SymId::articStaccatoAbove, SymId::articStaccatoBelow },
     762              :     { SymId::articAccentStaccatoAbove, SymId::articAccentStaccatoBelow },
     763              :     { SymId::articMarcatoStaccatoAbove, SymId::articMarcatoStaccatoBelow },
     764              :     { SymId::articTenutoStaccatoAbove, SymId::articTenutoStaccatoBelow },
     765              :     { SymId::articTenutoAccentAbove, SymId::articTenutoAccentBelow },
     766              :     { SymId::articMarcatoTenutoAbove, SymId::articMarcatoTenutoBelow },
     767              :     { SymId::articTenutoAbove, SymId::articTenutoBelow },
     768              :     { SymId::articMarcatoAbove, SymId::articMarcatoBelow },
     769              :     { SymId::articAccentAbove, SymId::articAccentBelow }
     770              : };
     771              : 
     772          307 : std::set<SymId> splitArticulations(const std::set<SymId>& articulationSymbolIds)
     773              : {
     774          307 :     std::set<SymId> result;
     775          614 :     for (const SymId& articulationSymbolId : articulationSymbolIds) {
     776          307 :         auto artic = ARTICULATION_ABOVE_SPLIT_GROUPS.find(articulationSymbolId);
     777          307 :         if (artic != ARTICULATION_ABOVE_SPLIT_GROUPS.end()) {
     778              :             // check above
     779           46 :             const ArticulationGroup& group = artic->second;
     780           46 :             result.insert(group.first);
     781           46 :             result.insert(group.second);
     782              :         } else {
     783              :             // check below
     784          261 :             artic = ARTICULATION_BELOW_SPLIT_GROUP.find(articulationSymbolId);
     785          261 :             if (artic != ARTICULATION_BELOW_SPLIT_GROUP.end()) {
     786           28 :                 const ArticulationGroup& group = artic->second;
     787           28 :                 result.insert(group.first);
     788           28 :                 result.insert(group.second);
     789              :             } else {
     790          233 :                 result.insert(articulationSymbolId);
     791              :             }
     792              :         }
     793              :     }
     794              : 
     795          307 :     return result;
     796            0 : }
     797              : 
     798            0 : std::set<SymId> joinArticulations(const std::set<SymId>& articulationSymbolIds)
     799              : {
     800            0 :     SymIdList result;
     801              : 
     802            0 :     SymIdList vsymbolIds(articulationSymbolIds.begin(), articulationSymbolIds.end());
     803              : 
     804            0 :     std::sort(vsymbolIds.begin(), vsymbolIds.end(), [](SymId l, SymId r) {
     805            0 :         return l > r;
     806              :     });
     807              : 
     808            0 :     std::set<SymId> splittedSymbols;
     809              : 
     810            0 :     auto symbolSelected = [&splittedSymbols](const SymId& symbolId) -> bool {
     811            0 :         return splittedSymbols.find(symbolId) != splittedSymbols.end();
     812            0 :     };
     813              : 
     814            0 :     for (size_t i = 0; i < vsymbolIds.size(); i++) {
     815            0 :         if (symbolSelected(vsymbolIds[i])) {
     816            0 :             continue;
     817              :         }
     818              : 
     819            0 :         bool found = false;
     820            0 :         for (size_t j = i + 1; j < vsymbolIds.size(); j++) {
     821            0 :             if (symbolSelected(vsymbolIds[i]) || symbolSelected(vsymbolIds[j])) {
     822            0 :                 continue;
     823              :             }
     824              : 
     825            0 :             ArticulationGroup group = { vsymbolIds[i], vsymbolIds[j] };
     826            0 :             auto joinArticulation = ARTICULATION_ABOVE_JOIN_GROUPS.find(group);
     827            0 :             if (joinArticulation != ARTICULATION_ABOVE_JOIN_GROUPS.end()) {
     828            0 :                 result.push_back(joinArticulation->second);
     829            0 :                 splittedSymbols.insert(vsymbolIds[i]);
     830            0 :                 splittedSymbols.insert(vsymbolIds[j]);
     831            0 :                 found = true;
     832              :             }
     833              :         }
     834              : 
     835            0 :         if (!found) {
     836            0 :             result.push_back(vsymbolIds[i]);
     837            0 :             splittedSymbols.insert(vsymbolIds[i]);
     838              :         }
     839              :     }
     840              : 
     841            0 :     std::sort(result.begin(), result.end(), [](SymId l, SymId r) {
     842            0 :         return l < r;
     843              :     });
     844              : 
     845            0 :     return std::set<SymId>(result.begin(), result.end());
     846            0 : }
     847              : 
     848            0 : std::set<SymId> updateArticulations(const std::set<SymId>& articulationSymbolIds, SymId articulationSymbolId,
     849              :                                     ArticulationsUpdateMode updateMode)
     850              : {
     851            0 :     std::set<SymId> splittedArticulations = splitArticulations(articulationSymbolIds);
     852              : 
     853            0 :     switch (articulationSymbolId) {
     854            0 :     case SymId::articMarcatoAbove: {
     855            0 :         if (splittedArticulations.find(SymId::articAccentAbove) != splittedArticulations.end()) {
     856            0 :             splittedArticulations.erase(SymId::articAccentAbove);
     857            0 :             splittedArticulations.insert(SymId::articMarcatoAbove);
     858            0 :         } else if (splittedArticulations.find(SymId::articMarcatoAbove) != splittedArticulations.end()) {
     859            0 :             if (updateMode == ArticulationsUpdateMode::Remove) {
     860            0 :                 splittedArticulations.erase(SymId::articMarcatoAbove);
     861              :             }
     862              :         } else {
     863            0 :             splittedArticulations.insert(SymId::articMarcatoAbove);
     864              :         }
     865            0 :         break;
     866              :     }
     867            0 :     case SymId::articAccentAbove: {
     868            0 :         if (splittedArticulations.find(SymId::articMarcatoAbove) != splittedArticulations.end()) {
     869            0 :             splittedArticulations.erase(SymId::articMarcatoAbove);
     870            0 :             splittedArticulations.insert(SymId::articAccentAbove);
     871            0 :         } else if (splittedArticulations.find(SymId::articAccentAbove) != splittedArticulations.end()) {
     872            0 :             if (updateMode == ArticulationsUpdateMode::Remove) {
     873            0 :                 splittedArticulations.erase(SymId::articAccentAbove);
     874              :             }
     875              :         } else {
     876            0 :             splittedArticulations.insert(SymId::articAccentAbove);
     877              :         }
     878            0 :         break;
     879              :     }
     880            0 :     case SymId::articTenutoAbove: {
     881            0 :         if (splittedArticulations.find(SymId::articTenutoAbove) != splittedArticulations.end()) {
     882            0 :             if (updateMode == ArticulationsUpdateMode::Remove) {
     883            0 :                 splittedArticulations.erase(SymId::articTenutoAbove);
     884              :             }
     885              :         } else {
     886            0 :             splittedArticulations.insert(SymId::articTenutoAbove);
     887              :         }
     888            0 :         break;
     889              :     }
     890            0 :     case SymId::articStaccatoAbove: {
     891            0 :         if (splittedArticulations.find(SymId::articStaccatoAbove) != splittedArticulations.end()) {
     892            0 :             if (updateMode == ArticulationsUpdateMode::Remove) {
     893            0 :                 splittedArticulations.erase(SymId::articStaccatoAbove);
     894              :             }
     895              :         } else {
     896            0 :             splittedArticulations.insert(SymId::articStaccatoAbove);
     897              :         }
     898            0 :         break;
     899              :     }
     900            0 :     default:
     901            0 :         break;
     902              :     }
     903              :     // we no longer want to join articulations when adding or removing individual ones. combined articulations can still
     904              :     // be added by the palette (which doesn't perform any of this splitting)
     905            0 :     return splittedArticulations;
     906            0 : }
     907              : 
     908            0 : std::set<SymId> flipArticulations(const std::set<SymId>& articulationSymbolIds, PlacementV placement)
     909              : {
     910            0 :     std::set<SymId> result;
     911            0 :     switch (placement) {
     912            0 :     case PlacementV::ABOVE:
     913            0 :         for (const SymId& articulationSymbolId: articulationSymbolIds) {
     914            0 :             bool found = false;
     915            0 :             for (auto it = ARTICULATION_PLACEMENTS.begin(); it != ARTICULATION_PLACEMENTS.end(); ++it) {
     916            0 :                 if (it->second == articulationSymbolId) {
     917            0 :                     result.insert(it->first);
     918            0 :                     found = true;
     919            0 :                     break;
     920              :                 }
     921              :             }
     922              : 
     923            0 :             if (!found) {
     924            0 :                 result.insert(articulationSymbolId);
     925              :             }
     926              :         }
     927            0 :         break;
     928            0 :     case PlacementV::BELOW:
     929            0 :         for (const SymId& articulationSymbolId: articulationSymbolIds) {
     930            0 :             bool found = false;
     931            0 :             for (auto it = ARTICULATION_PLACEMENTS.begin(); it != ARTICULATION_PLACEMENTS.end(); ++it) {
     932            0 :                 if (it->first == articulationSymbolId) {
     933            0 :                     result.insert(it->second);
     934            0 :                     found = true;
     935            0 :                     break;
     936              :                 }
     937              :             }
     938              : 
     939            0 :             if (!found) {
     940            0 :                 result.insert(articulationSymbolId);
     941              :             }
     942              :         }
     943              :     }
     944              : 
     945            0 :     return result;
     946            0 : }
     947              : 
     948         3454 : double Articulation::LayoutData::opticalCenter() const
     949              : {
     950         3454 :     switch (symId.value()) {
     951            4 :     case SymId::handbellsMartellatoLift:
     952            4 :         return 0.5 * m_item->symWidth(SymId::handbellsMartellato);
     953            8 :     case SymId::handbellsMalletLft:
     954            8 :         return 0.5 * m_item->symWidth(SymId::handbellsMalletBellOnTable);
     955            4 :     case SymId::handbellsPluckLift:
     956            4 :         return 0.5 * m_item->symWidth(SymId::articStaccatoAbove);
     957         3438 :     default:
     958         3438 :         return 0.5 * bbox().width();
     959              :     }
     960              : }
     961              : }
        

Generated by: LCOV version 2.0-1