LCOV - code coverage report
Current view: top level - src/engraving/dom - ottava.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 82.6 % 172 142
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 "ottava.h"
      24              : 
      25              : #include "types/translatablestring.h"
      26              : 
      27              : #include "chordrest.h"
      28              : #include "measure.h"
      29              : #include "score.h"
      30              : #include "staff.h"
      31              : #include "system.h"
      32              : #include "text.h"
      33              : 
      34              : #include "log.h"
      35              : 
      36              : using namespace mu::engraving;
      37              : 
      38              : //---------------------------------------------------------
      39              : //   ottavaStyle
      40              : //---------------------------------------------------------
      41              : 
      42              : static const ElementStyle ottavaStyle {
      43              :     { Sid::ottavaNumbersOnly,                  Pid::NUMBERS_ONLY },
      44              :     { Sid::ottava8VAPlacement,                 Pid::PLACEMENT },
      45              :     { Sid::ottava8VAText,                      Pid::BEGIN_TEXT },
      46              :     { Sid::ottava8VAContinueText,              Pid::CONTINUE_TEXT },
      47              :     { Sid::ottavaHookAbove,                    Pid::END_HOOK_HEIGHT },
      48              :     { Sid::ottavaFontFace,                     Pid::BEGIN_FONT_FACE },
      49              :     { Sid::ottavaFontFace,                     Pid::CONTINUE_FONT_FACE },
      50              :     { Sid::ottavaFontFace,                     Pid::END_FONT_FACE },
      51              :     { Sid::ottavaFontSize,                     Pid::BEGIN_FONT_SIZE },
      52              :     { Sid::ottavaFontSize,                     Pid::CONTINUE_FONT_SIZE },
      53              :     { Sid::ottavaFontSize,                     Pid::END_FONT_SIZE },
      54              :     { Sid::ottavaFontStyle,                    Pid::BEGIN_FONT_STYLE },
      55              :     { Sid::ottavaFontStyle,                    Pid::CONTINUE_FONT_STYLE },
      56              :     { Sid::ottavaFontStyle,                    Pid::END_FONT_STYLE },
      57              :     { Sid::ottavaMusicalSymbolsScale,          Pid::BEGIN_TEXT_MUSICAL_SYMBOLS_SCALE },
      58              :     { Sid::ottavaMusicalSymbolsScale,          Pid::CONTINUE_TEXT_MUSICAL_SYMBOLS_SCALE },
      59              :     { Sid::ottavaMusicalSymbolsScale,          Pid::END_TEXT_MUSICAL_SYMBOLS_SCALE },
      60              :     { Sid::dummyMusicalSymbolSize,             Pid::BEGIN_TEXT_MUSIC_SYMBOLS_SIZE },
      61              :     { Sid::dummyMusicalSymbolSize,             Pid::CONTINUE_TEXT_MUSIC_SYMBOLS_SIZE },
      62              :     { Sid::dummyMusicalSymbolSize,             Pid::END_TEXT_MUSIC_SYMBOLS_SIZE },
      63              :     { Sid::ottavaTextAlignAbove,               Pid::BEGIN_TEXT_ALIGN },
      64              :     { Sid::ottavaTextAlignAbove,               Pid::CONTINUE_TEXT_ALIGN },
      65              :     { Sid::ottavaTextAlignAbove,               Pid::END_TEXT_ALIGN },
      66              :     { Sid::ottavaPosition,                     Pid::BEGIN_TEXT_POSITION },
      67              :     { Sid::ottavaPosition,                     Pid::CONTINUE_TEXT_POSITION },
      68              :     { Sid::ottavaPosition,                     Pid::END_TEXT_POSITION },
      69              :     { Sid::ottavaLineWidth,                    Pid::LINE_WIDTH },
      70              :     { Sid::ottavaLineStyle,                    Pid::LINE_STYLE },
      71              :     { Sid::ottavaDashLineLen,                  Pid::DASH_LINE_LEN },
      72              :     { Sid::ottavaDashGapLen,                   Pid::DASH_GAP_LEN },
      73              :     { Sid::ottavaFontSpatiumDependent,         Pid::TEXT_SIZE_SPATIUM_DEPENDENT },
      74              :     { Sid::ottavaEndLineArrowHeight,           Pid::END_LINE_ARROW_HEIGHT },
      75              :     { Sid::ottavaEndLineArrowWidth,            Pid::END_LINE_ARROW_WIDTH },
      76              :     { Sid::ottavaBeginLineArrowHeight,         Pid::BEGIN_LINE_ARROW_HEIGHT },
      77              :     { Sid::ottavaBeginLineArrowWidth,          Pid::BEGIN_LINE_ARROW_WIDTH },
      78              :     { Sid::ottavaEndFilledArrowHeight,         Pid::END_FILLED_ARROW_HEIGHT },
      79              :     { Sid::ottavaEndFilledArrowWidth,          Pid::END_FILLED_ARROW_WIDTH },
      80              :     { Sid::ottavaBeginFilledArrowHeight,       Pid::BEGIN_FILLED_ARROW_HEIGHT },
      81              :     { Sid::ottavaBeginFilledArrowWidth,        Pid::BEGIN_FILLED_ARROW_WIDTH },
      82              : };
      83              : 
      84          110 : OttavaSegment::OttavaSegment(Ottava* sp)
      85          110 :     : TextLineBaseSegment(ElementType::OTTAVA_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      86              : {
      87          110 :     m_text->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      88          110 :     m_endText->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      89          110 : }
      90              : 
      91              : //---------------------------------------------------------
      92              : //   propertyDelegate
      93              : //---------------------------------------------------------
      94              : 
      95          883 : EngravingObject* OttavaSegment::propertyDelegate(Pid pid) const
      96              : {
      97          883 :     if (pid == Pid::OTTAVA_TYPE || pid == Pid::NUMBERS_ONLY) {
      98            0 :         return spanner();
      99              :     }
     100          883 :     return TextLineBaseSegment::propertyDelegate(pid);
     101              : }
     102              : 
     103              : //---------------------------------------------------------
     104              : //   setOttavaType
     105              : //---------------------------------------------------------
     106              : 
     107          252 : void Ottava::setOttavaType(OttavaType val)
     108              : {
     109          252 :     if (m_ottavaType == val) {
     110          213 :         return;
     111              :     }
     112           39 :     m_ottavaType = val;
     113           39 :     styleChanged();
     114              : }
     115              : 
     116              : //---------------------------------------------------------
     117              : //   setNumbersOnly
     118              : //---------------------------------------------------------
     119              : 
     120            0 : void Ottava::setNumbersOnly(bool val)
     121              : {
     122            0 :     m_numbersOnly = val;
     123            0 : }
     124              : 
     125              : //---------------------------------------------------------
     126              : //   setPlacement
     127              : //---------------------------------------------------------
     128              : 
     129            0 : void Ottava::setPlacement(PlacementV p)
     130              : {
     131            0 :     TextLineBase::setPlacement(p);
     132            0 : }
     133              : 
     134              : //---------------------------------------------------------
     135              : //   undoChangeProperty
     136              : //---------------------------------------------------------
     137              : 
     138            0 : void OttavaSegment::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
     139              : {
     140            0 :     if (id == Pid::OTTAVA_TYPE || id == Pid::NUMBERS_ONLY) {
     141            0 :         EngravingObject::undoChangeProperty(id, v, ps);
     142              :     } else {
     143            0 :         EngravingObject::undoChangeProperty(id, v, ps);
     144              :     }
     145            0 : }
     146              : 
     147            5 : void Ottava::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
     148              : {
     149            5 :     if (id == Pid::OTTAVA_TYPE || id == Pid::NUMBERS_ONLY) {
     150            0 :         TextLineBase::undoChangeProperty(id, v, ps);
     151            0 :         styleChanged();       // these properties may change style settings
     152              :     } else {
     153            5 :         TextLineBase::undoChangeProperty(id, v, ps);
     154              :     }
     155            5 : }
     156              : 
     157         9491 : Sid Ottava::getPropertyStyle(Pid pid) const
     158              : {
     159              :     static_assert(int(OttavaType::OTTAVA_22MB) - int(OttavaType::OTTAVA_8VA) == 5);
     160              : 
     161              :     static const std::vector<Sid> ss = {
     162              :         Sid::ottava8VAPlacement,
     163              :         Sid::ottava8VAnoText,
     164              :         Sid::ottava8VAnoContinueText,
     165              :         Sid::ottava8VBPlacement,
     166              :         Sid::ottava8VBnoText,
     167              :         Sid::ottava8VBnoContinueText,
     168              :         Sid::ottava15MAPlacement,
     169              :         Sid::ottava15MAnoText,
     170              :         Sid::ottava15MAnoContinueText,
     171              :         Sid::ottava15MBPlacement,
     172              :         Sid::ottava15MBnoText,
     173              :         Sid::ottava15MBnoContinueText,
     174              :         Sid::ottava22MAPlacement,
     175              :         Sid::ottava22MAnoText,
     176              :         Sid::ottava22MAnoContinueText,
     177              :         Sid::ottava22MBPlacement,
     178              :         Sid::ottava22MBnoText,
     179              :         Sid::ottava22MBnoContinueText,
     180              : 
     181              :         Sid::ottava8VAPlacement,
     182              :         Sid::ottava8VAText,
     183              :         Sid::ottava8VAContinueText,
     184              :         Sid::ottava8VBPlacement,
     185              :         Sid::ottava8VBText,
     186              :         Sid::ottava8VBContinueText,
     187              :         Sid::ottava15MAPlacement,
     188              :         Sid::ottava15MAText,
     189              :         Sid::ottava15MAContinueText,
     190              :         Sid::ottava15MBPlacement,
     191              :         Sid::ottava15MBText,
     192              :         Sid::ottava15MBContinueText,
     193              :         Sid::ottava22MAPlacement,
     194              :         Sid::ottava22MAText,
     195              :         Sid::ottava22MAContinueText,
     196              :         Sid::ottava22MBPlacement,
     197              :         Sid::ottava22MBText,
     198              :         Sid::ottava22MBContinueText,
     199         9501 :     };
     200              : 
     201         9491 :     size_t idx = size_t(m_ottavaType) * 3 + (m_numbersOnly ? 0 : ss.size() / 2);
     202         9491 :     switch (pid) {
     203          243 :     case Pid::PLACEMENT:
     204          243 :         return ss[idx];
     205          239 :     case Pid::BEGIN_TEXT:
     206          239 :         return ss[idx + 1];               // BEGIN_TEXT
     207          239 :     case Pid::CONTINUE_TEXT:
     208          239 :         return ss[idx + 2];               // CONTINUE_TEXT
     209          239 :     case Pid::END_HOOK_HEIGHT:
     210          239 :         if (isStyled(Pid::PLACEMENT)) {
     211          238 :             return style().styleV(ss[idx]).value<PlacementV>() == PlacementV::ABOVE
     212          238 :                    ? Sid::ottavaHookAbove
     213          238 :                    : Sid::ottavaHookBelow;
     214              :         } else {
     215            1 :             return placeAbove() ? Sid::ottavaHookAbove : Sid::ottavaHookBelow;
     216              :         }
     217          714 :     case Pid::BEGIN_TEXT_ALIGN:
     218              :     case Pid::CONTINUE_TEXT_ALIGN:
     219              :     case Pid::END_TEXT_ALIGN:
     220          714 :         return placeAbove() ? Sid::ottavaTextAlignAbove : Sid::ottavaTextAlignBelow;
     221         7817 :     default:
     222         7817 :         return TextLineBase::getPropertyStyle(pid);
     223              :     }
     224              : }
     225              : 
     226              : //---------------------------------------------------------
     227              : //   Ottava
     228              : //---------------------------------------------------------
     229              : 
     230          187 : Ottava::Ottava(DummyParentOr<EngravingItem> parent)
     231          187 :     : TextLineBase(ElementType::OTTAVA, parent, ElementFlag::ON_STAFF | ElementFlag::MOVABLE)
     232              : {
     233          187 :     m_ottavaType  = OttavaType::OTTAVA_8VA;
     234          187 :     m_numbersOnly = false;
     235          187 :     setBeginTextPlace(TextPlace::LEFT);
     236          187 :     setContinueTextPlace(TextPlace::LEFT);
     237          187 :     setEndHookType(HookType::HOOK_90);
     238          187 :     setLineVisible(true);
     239          187 :     setBeginHookHeight(0_sp);
     240          187 :     setEndText(u"");
     241              : 
     242          187 :     initElementStyle(&ottavaStyle);
     243          187 : }
     244              : 
     245            4 : Ottava::Ottava(const Ottava& o)
     246            4 :     : TextLineBase(o)
     247              : {
     248            4 :     setOttavaType(o.m_ottavaType);
     249            4 :     m_numbersOnly = o.m_numbersOnly;
     250            4 : }
     251              : 
     252              : //---------------------------------------------------------
     253              : //   pitchShift
     254              : //---------------------------------------------------------
     255              : 
     256          223 : int Ottava::pitchShift() const
     257              : {
     258          223 :     return ottavaDefault[int(m_ottavaType)].shift;
     259              : }
     260              : 
     261              : //---------------------------------------------------------
     262              : //   createLineSegment
     263              : //---------------------------------------------------------
     264              : 
     265              : static const ElementStyle ottavaSegmentStyle {
     266              :     { Sid::ottavaMinDistance, Pid::MIN_DISTANCE },
     267              : };
     268              : 
     269          110 : LineSegment* Ottava::createLineSegment()
     270              : {
     271          110 :     OttavaSegment* os = new OttavaSegment(this);
     272          110 :     os->setTrack(track());
     273          110 :     os->initElementStyle(&ottavaSegmentStyle);
     274          110 :     return os;
     275              : }
     276              : 
     277              : //---------------------------------------------------------
     278              : //   getProperty
     279              : //---------------------------------------------------------
     280              : 
     281         2814 : PropertyValue Ottava::getProperty(Pid propertyId) const
     282              : {
     283         2814 :     switch (propertyId) {
     284          108 :     case Pid::OTTAVA_TYPE:
     285          108 :         return int(ottavaType());
     286              : 
     287            6 :     case Pid::NUMBERS_ONLY:
     288            6 :         return m_numbersOnly;
     289              : 
     290          108 :     case Pid::END_TEXT_PLACE:                         // HACK
     291          108 :         return TextPlace::LEFT;
     292              : 
     293         2592 :     default:
     294         2592 :         break;
     295              :     }
     296         2592 :     return TextLineBase::getProperty(propertyId);
     297              : }
     298              : 
     299              : //---------------------------------------------------------
     300              : //   setProperty
     301              : //---------------------------------------------------------
     302              : 
     303         9359 : bool Ottava::setProperty(Pid propertyId, const PropertyValue& val)
     304              : {
     305         9359 :     switch (propertyId) {
     306            0 :     case Pid::PLAY:
     307            0 :         setPlaySpanner(val.toBool());
     308            0 :         staff()->updateOttava();
     309            0 :         break;
     310              : 
     311            4 :     case Pid::OTTAVA_TYPE:
     312            4 :         setOttavaType(OttavaType(val.toInt()));
     313            4 :         break;
     314              : 
     315          238 :     case Pid::NUMBERS_ONLY:
     316          238 :         m_numbersOnly = val.toBool();
     317          238 :         break;
     318              : 
     319            4 :     case Pid::SPANNER_TICKS:
     320            4 :         setTicks(val.value<Fraction>());
     321            4 :         staff()->updateOttava();
     322            4 :         break;
     323              : 
     324            2 :     case Pid::SPANNER_TICK:
     325            2 :         setTick(val.value<Fraction>());
     326            2 :         staff()->updateOttava();
     327            2 :         break;
     328              : 
     329         9111 :     default:
     330         9111 :         if (!TextLineBase::setProperty(propertyId, val)) {
     331            0 :             return false;
     332              :         }
     333         9111 :         break;
     334              :     }
     335         9359 :     triggerLayout();
     336         9359 :     return true;
     337              : }
     338              : 
     339              : //---------------------------------------------------------
     340              : //   propertyDefault
     341              : //---------------------------------------------------------
     342              : 
     343         2902 : PropertyValue Ottava::propertyDefault(Pid pid) const
     344              : {
     345         2902 :     switch (pid) {
     346          108 :     case Pid::OTTAVA_TYPE:
     347          108 :         return PropertyValue();
     348          108 :     case Pid::END_HOOK_TYPE:
     349          108 :         return HookType::HOOK_90;
     350          108 :     case Pid::LINE_VISIBLE:
     351          108 :         return true;
     352          324 :     case Pid::BEGIN_TEXT_OFFSET:
     353              :     case Pid::CONTINUE_TEXT_OFFSET:
     354              :     case Pid::END_TEXT_OFFSET:
     355          324 :         return PropertyValue::fromValue(PointF());
     356          324 :     case Pid::BEGIN_TEXT_PLACE:
     357              :     case Pid::CONTINUE_TEXT_PLACE:
     358              :     case Pid::END_TEXT_PLACE:
     359          324 :         return TextPlace::LEFT;
     360          108 :     case Pid::BEGIN_HOOK_TYPE:
     361          108 :         return HookType::NONE;
     362          108 :     case Pid::BEGIN_HOOK_HEIGHT:
     363          108 :         return 0_sp;
     364          108 :     case Pid::END_TEXT:
     365          216 :         return String();
     366            7 :     case Pid::PLACEMENT:
     367            7 :         return styleValue(Pid::PLACEMENT, getPropertyStyle(Pid::PLACEMENT));
     368          220 :     case Pid::TEXT_STYLE:
     369          220 :         return TextStyleType::OTTAVA;
     370              : 
     371         1379 :     default:
     372         1379 :         return TextLineBase::propertyDefault(pid);
     373              :     }
     374              : }
     375              : 
     376              : //---------------------------------------------------------
     377              : //   accessibleInfo
     378              : //---------------------------------------------------------
     379              : 
     380            0 : String Ottava::accessibleInfo() const
     381              : {
     382            0 :     return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), String::fromUtf8(ottavaDefault[static_cast<int>(ottavaType())].name));
     383              : }
     384              : 
     385              : //---------------------------------------------------------
     386              : //   subtypeUserName
     387              : //---------------------------------------------------------
     388              : 
     389            0 : muse::TranslatableString Ottava::subtypeUserName() const
     390              : {
     391            0 :     return ottavaDefault[int(ottavaType())].userName;
     392              : }
     393              : 
     394            0 : void OttavaSegment::rebaseOffsetsOnAnchorChanged(Grip grip, const PointF& oldPos, System* sys)
     395              : {
     396            0 :     if (grip == Grip::MIDDLE || grip == Grip::END) {
     397            0 :         ottava()->computeEndElement();
     398              :     }
     399            0 :     LineSegment::rebaseOffsetsOnAnchorChanged(grip, oldPos, sys);
     400            0 : }
     401              : 
     402              : //---------------------------------------------------------
     403              : //   ottavaTypeName
     404              : //---------------------------------------------------------
     405              : 
     406          108 : const char* Ottava::ottavaTypeName(OttavaType type)
     407              : {
     408          108 :     return ottavaDefault[int(type)].name;
     409              : }
     410              : 
     411          308 : PointF Ottava::linePos(Grip grip, System** system) const
     412              : {
     413          308 :     if (grip == Grip::START) {
     414          155 :         return TextLineBase::linePos(grip, system);
     415              :     }
     416              : 
     417          153 :     bool extendToEndOfDuration = false; // TODO: style
     418          153 :     if (extendToEndOfDuration) {
     419            0 :         return SLine::linePos(grip, system);
     420              :     }
     421              : 
     422          153 :     ChordRest* endCr = endElement() && endElement()->isChordRest() ? toChordRest(endElement()) : nullptr;
     423          153 :     if (!endCr) {
     424            0 :         return PointF();
     425              :     }
     426              : 
     427          153 :     Segment* seg = endCr->segment();
     428              : 
     429          153 :     *system = seg->measure()->system();
     430              : 
     431              :     // End 1sp after the right edge of the end chord, but don't overlap followig segments
     432          153 :     Shape staffShape = seg->staffShape(endCr->vStaffIdx());
     433          556 :     staffShape.remove_if([](ShapeElement& el) { return el.height() == 0; });
     434          153 :     double x = staffShape.right() + seg->x() + seg->measure()->x() + spatium();
     435          153 :     Segment* followingCRseg = score()->tick2segment(endCr->endTick(), true, SegmentType::ChordRest);
     436          153 :     if (followingCRseg && followingCRseg->system() == seg->system()) {
     437          141 :         x = std::min(x, followingCRseg->x() + followingCRseg->measure()->x());
     438              :     }
     439              : 
     440          153 :     x -= 0.5 * absoluteFromSpatium(lineWidth());
     441              : 
     442          153 :     return PointF(x, 0.0);
     443          153 : }
     444              : 
     445          313 : void Ottava::doComputeEndElement()
     446              : {
     447          313 :     setEndElement(score()->findChordRestEndingBeforeTickInStaff(tick2(), track2staff(track())));
     448          313 : }
     449              : 
     450          165 : Sid Ottava::defaultPosSid() const
     451              : {
     452          165 :     return placeAbove() ? Sid::ottavaPosAbove : Sid::ottavaPosBelow;
     453              : }
        

Generated by: LCOV version 2.0-1