LCOV - code coverage report
Current view: top level - src/engraving/dom - volta.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 84.4 % 167 141
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 "volta.h"
      24              : 
      25              : #include <algorithm>
      26              : #include <vector>
      27              : 
      28              : #include "barline.h"
      29              : #include "keysig.h"
      30              : #include "measure.h"
      31              : #include "score.h"
      32              : #include "staff.h"
      33              : #include "system.h"
      34              : #include "text.h"
      35              : 
      36              : #include "log.h"
      37              : 
      38              : using namespace mu;
      39              : using namespace mu::engraving;
      40              : 
      41              : namespace mu::engraving {
      42              : static const ElementStyle voltaStyle {
      43              :     { Sid::voltaFontFace,                      Pid::BEGIN_FONT_FACE },
      44              :     { Sid::voltaFontFace,                      Pid::CONTINUE_FONT_FACE },
      45              :     { Sid::voltaFontFace,                      Pid::END_FONT_FACE },
      46              :     { Sid::voltaFontSize,                      Pid::BEGIN_FONT_SIZE },
      47              :     { Sid::voltaFontSize,                      Pid::CONTINUE_FONT_SIZE },
      48              :     { Sid::voltaFontSize,                      Pid::END_FONT_SIZE },
      49              :     { Sid::voltaFontStyle,                     Pid::BEGIN_FONT_STYLE },
      50              :     { Sid::voltaFontStyle,                     Pid::CONTINUE_FONT_STYLE },
      51              :     { Sid::voltaFontStyle,                     Pid::END_FONT_STYLE },
      52              :     { Sid::voltaAlign,                         Pid::BEGIN_TEXT_ALIGN },
      53              :     { Sid::voltaAlign,                         Pid::CONTINUE_TEXT_ALIGN },
      54              :     { Sid::voltaAlign,                         Pid::END_TEXT_ALIGN },
      55              :     { Sid::voltaPosition,                      Pid::BEGIN_TEXT_POSITION },
      56              :     { Sid::voltaPosition,                      Pid::CONTINUE_TEXT_POSITION },
      57              :     { Sid::voltaPosition,                      Pid::END_TEXT_POSITION },
      58              :     { Sid::voltaOffset,                        Pid::BEGIN_TEXT_OFFSET },
      59              :     { Sid::voltaOffset,                        Pid::CONTINUE_TEXT_OFFSET },
      60              :     { Sid::voltaOffset,                        Pid::END_TEXT_OFFSET },
      61              :     { Sid::voltaMusicalSymbolSize,             Pid::BEGIN_TEXT_MUSIC_SYMBOLS_SIZE },
      62              :     { Sid::voltaMusicalSymbolSize,             Pid::CONTINUE_TEXT_MUSIC_SYMBOLS_SIZE },
      63              :     { Sid::voltaMusicalSymbolSize,             Pid::END_TEXT_MUSIC_SYMBOLS_SIZE },
      64              :     { Sid::dummyMusicalSymbolsScale,           Pid::BEGIN_TEXT_MUSICAL_SYMBOLS_SCALE },
      65              :     { Sid::dummyMusicalSymbolsScale,           Pid::CONTINUE_TEXT_MUSICAL_SYMBOLS_SCALE },
      66              :     { Sid::dummyMusicalSymbolsScale,           Pid::END_TEXT_MUSICAL_SYMBOLS_SCALE },
      67              :     { Sid::voltaLineWidth,                     Pid::LINE_WIDTH },
      68              :     { Sid::voltaLineStyle,                     Pid::LINE_STYLE },
      69              :     { Sid::voltaDashLineLen,                   Pid::DASH_LINE_LEN },
      70              :     { Sid::voltaDashGapLen,                    Pid::DASH_GAP_LEN },
      71              :     { Sid::voltaHook,                          Pid::BEGIN_HOOK_HEIGHT },
      72              :     { Sid::voltaHook,                          Pid::END_HOOK_HEIGHT },
      73              :     { Sid::voltaFontSpatiumDependent,          Pid::TEXT_SIZE_SPATIUM_DEPENDENT },
      74              : };
      75              : 
      76              : //---------------------------------------------------------
      77              : //   VoltaSegment
      78              : //---------------------------------------------------------
      79              : 
      80          180 : VoltaSegment::VoltaSegment(Volta* sp)
      81          180 :     : TextLineBaseSegment(ElementType::VOLTA_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF | ElementFlag::SYSTEM)
      82              : {
      83          180 :     m_text->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      84          180 :     m_endText->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      85          180 : }
      86              : 
      87              : //---------------------------------------------------------
      88              : //   propertyDelegate
      89              : //---------------------------------------------------------
      90              : 
      91         1292 : EngravingObject* VoltaSegment::propertyDelegate(Pid pid) const
      92              : {
      93         1292 :     if (pid == Pid::BEGIN_HOOK_TYPE || pid == Pid::END_HOOK_TYPE || pid == Pid::VOLTA_ENDING) {
      94            0 :         return spanner();
      95              :     }
      96         1292 :     return TextLineBaseSegment::propertyDelegate(pid);
      97              : }
      98              : 
      99              : //---------------------------------------------------------
     100              : //   Volta
     101              : //---------------------------------------------------------
     102              : 
     103          176 : Volta::Volta(DummyParentOr<EngravingItem> parent)
     104          176 :     : TextLineBase(ElementType::VOLTA, parent, ElementFlag::SYSTEM)
     105              : {
     106          176 :     setPlacement(PlacementV::ABOVE);
     107          176 :     initElementStyle(&voltaStyle);
     108              : 
     109          176 :     setBeginTextPlace(TextPlace::BELOW);
     110          176 :     setContinueTextPlace(TextPlace::BELOW);
     111          176 :     setLineVisible(true);
     112          176 :     resetProperty(Pid::BEGIN_TEXT);
     113          176 :     resetProperty(Pid::CONTINUE_TEXT);
     114          176 :     resetProperty(Pid::END_TEXT);
     115          176 :     resetProperty(Pid::BEGIN_TEXT_PLACE);
     116          176 :     resetProperty(Pid::CONTINUE_TEXT_PLACE);
     117          176 :     resetProperty(Pid::END_TEXT_PLACE);
     118          176 :     resetProperty(Pid::BEGIN_HOOK_TYPE);
     119          176 :     resetProperty(Pid::END_HOOK_TYPE);
     120          176 : }
     121              : 
     122              : ///
     123              : /// \brief sorts the provided list in ascending order
     124              : ///
     125          148 : void Volta::setEndings(const std::vector<int>& l)
     126              : {
     127          148 :     m_endings = l;
     128          148 :     std::sort(m_endings.begin(), m_endings.end());
     129          148 : }
     130              : 
     131              : //---------------------------------------------------------
     132              : //   setText
     133              : //---------------------------------------------------------
     134              : 
     135           57 : void Volta::setText(const String& s)
     136              : {
     137           57 :     setBeginText(s);
     138           57 : }
     139              : 
     140              : //---------------------------------------------------------
     141              : //   text
     142              : //---------------------------------------------------------
     143              : 
     144           29 : String Volta::text() const
     145              : {
     146           29 :     return beginText();
     147              : }
     148              : 
     149              : //---------------------------------------------------------
     150              : //   createLineSegment
     151              : //---------------------------------------------------------
     152              : 
     153              : static const ElementStyle voltaSegmentStyle {
     154              :     { Sid::voltaMinDistance,                   Pid::MIN_DISTANCE },
     155              : };
     156              : 
     157          180 : LineSegment* Volta::createLineSegment()
     158              : {
     159          180 :     VoltaSegment* vs = new VoltaSegment(this);
     160          180 :     vs->setTrack(track());
     161          180 :     vs->initElementStyle(&voltaSegmentStyle);
     162          180 :     return vs;
     163              : }
     164              : 
     165              : //---------------------------------------------------------
     166              : //   hasEnding
     167              : //---------------------------------------------------------
     168              : 
     169          478 : bool Volta::hasEnding(int repeat) const
     170              : {
     171          825 :     for (int ending : endings()) {
     172          617 :         if (ending == repeat) {
     173          270 :             return true;
     174              :         }
     175          478 :     }
     176          208 :     return false;
     177              : }
     178              : 
     179              : //---------------------------------------------------------
     180              : //   firstEnding
     181              : //---------------------------------------------------------
     182              : 
     183            1 : int Volta::firstEnding() const
     184              : {
     185            1 :     if (m_endings.empty()) {
     186            0 :         return 0;
     187              :     }
     188            1 :     return m_endings.front();
     189              : }
     190              : 
     191              : //---------------------------------------------------------
     192              : //   lastEnding
     193              : //---------------------------------------------------------
     194              : 
     195           15 : int Volta::lastEnding() const
     196              : {
     197           15 :     if (m_endings.empty()) {
     198            0 :         return 0;
     199              :     }
     200           15 :     return m_endings.back();
     201              : }
     202              : 
     203              : //---------------------------------------------------------
     204              : //   getProperty
     205              : //---------------------------------------------------------
     206              : 
     207         1931 : PropertyValue Volta::getProperty(Pid propertyId) const
     208              : {
     209         1931 :     switch (propertyId) {
     210            0 :     case Pid::VOLTA_ENDING:
     211            0 :         return PropertyValue::fromValue(endings());
     212         1931 :     default:
     213         1931 :         break;
     214              :     }
     215         1931 :     return TextLineBase::getProperty(propertyId);
     216              : }
     217              : 
     218              : //---------------------------------------------------------
     219              : //   setProperty
     220              : //---------------------------------------------------------
     221              : 
     222         7093 : bool Volta::setProperty(Pid propertyId, const PropertyValue& val)
     223              : {
     224         7093 :     switch (propertyId) {
     225            0 :     case Pid::VOLTA_ENDING: {
     226            0 :         setEndings(val.value<std::vector<int> >());
     227            0 :     } break;
     228         7093 :     default:
     229         7093 :         if (!TextLineBase::setProperty(propertyId, val)) {
     230            0 :             return false;
     231              :         }
     232         7093 :         break;
     233              :     }
     234         7093 :     triggerLayout();
     235         7093 :     return true;
     236              : }
     237              : 
     238              : //---------------------------------------------------------
     239              : //   propertyDefault
     240              : //---------------------------------------------------------
     241              : 
     242         3643 : PropertyValue Volta::propertyDefault(Pid propertyId) const
     243              : {
     244         3643 :     switch (propertyId) {
     245            0 :     case Pid::VOLTA_ENDING:
     246            0 :         return PropertyValue::fromValue(std::vector<int>());
     247          231 :     case Pid::BEGIN_HOOK_TYPE:
     248          231 :         return HookType::HOOK_90;
     249          231 :     case Pid::END_HOOK_TYPE:
     250          231 :         return HookType::NONE;
     251          693 :     case Pid::BEGIN_TEXT:
     252              :     case Pid::CONTINUE_TEXT:
     253              :     case Pid::END_TEXT:
     254          693 :         return "";
     255           55 :     case Pid::LINE_VISIBLE:
     256           55 :         return true;
     257          693 :     case Pid::BEGIN_TEXT_PLACE:
     258              :     case Pid::CONTINUE_TEXT_PLACE:
     259              :     case Pid::END_TEXT_PLACE:
     260          693 :         return TextPlace::ABOVE;
     261              : 
     262           55 :     case Pid::PLACEMENT:
     263           55 :         return PlacementV::ABOVE;
     264              : 
     265          360 :     case Pid::TEXT_STYLE:
     266          360 :         return TextStyleType::VOLTA;
     267              : 
     268          440 :     case Pid::BEGIN_FILLED_ARROW_HEIGHT:   // No arrow endings for voltas
     269              :     case Pid::BEGIN_FILLED_ARROW_WIDTH:
     270              :     case Pid::END_FILLED_ARROW_HEIGHT:
     271              :     case Pid::END_FILLED_ARROW_WIDTH:
     272              :     case Pid::BEGIN_LINE_ARROW_HEIGHT:
     273              :     case Pid::BEGIN_LINE_ARROW_WIDTH:
     274              :     case Pid::END_LINE_ARROW_HEIGHT:
     275              :     case Pid::END_LINE_ARROW_WIDTH:
     276          440 :         return 0.0;
     277              : 
     278          885 :     default:
     279          885 :         return TextLineBase::propertyDefault(propertyId);
     280              :     }
     281              : }
     282              : 
     283              : //---------------------------------------------------------
     284              : //   setChannel
     285              : //---------------------------------------------------------
     286              : 
     287          125 : void Volta::setChannel() const
     288              : {
     289          125 :     Measure* startMeasure = Spanner::startMeasure();
     290          125 :     Measure* endMeasure = Spanner::endMeasure();
     291              : 
     292          125 :     if (startMeasure && endMeasure) {
     293          125 :         if (!endMeasure->repeatEnd()) {
     294           61 :             return;
     295              :         }
     296              : 
     297           64 :         Fraction startTick = startMeasure->tick() - Fraction::eps();
     298           64 :         Fraction endTick  = endMeasure->endTick() - Fraction::eps();
     299           64 :         Staff* st = staff();
     300          320 :         for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
     301          256 :             int channel = st->channel(startTick, voice);
     302          256 :             st->insertIntoChannelList(voice, endTick, channel);
     303              :         }
     304              :     }
     305              : }
     306              : 
     307              : //---------------------------------------------------------
     308              : //   accessibleInfo
     309              : //---------------------------------------------------------
     310              : 
     311            0 : String Volta::accessibleInfo() const
     312              : {
     313            0 :     return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), text());
     314              : }
     315              : 
     316          546 : PointF Volta::linePos(Grip grip, System** system) const
     317              : {
     318          546 :     bool start = grip == Grip::START;
     319              : 
     320          546 :     Segment* segment = score()->tick2leftSegment(start ? tick() : tick2(), true,
     321              :                                                  SegmentType::ChordRest | SegmentType::StartRepeatBarLine | SegmentType::EndBarLine);
     322          546 :     if (!segment) {
     323            0 :         return PointF();
     324              :     }
     325              : 
     326          546 :     const Measure* measure = segment->measure();
     327          546 :     bool isAtSystemStart = segment->rtick().isZero() && measure && measure->system() && measure->isFirstInSystem();
     328          546 :     bool searchForPrevBarline = start ? segment->rtick().isZero() && (measure->repeatStart() || !isAtSystemStart) : true;
     329              : 
     330          546 :     SegmentType barlineType = start ? (SegmentType::StartRepeatBarLine | SegmentType::EndBarLine) : SegmentType::EndBarLine;
     331              : 
     332          546 :     if (searchForPrevBarline) {
     333          522 :         Segment* prev = segment;
     334         1048 :         while (prev && !prev->isType(barlineType) && prev->tick() == segment->tick()) {
     335          526 :             prev = prev->prev1MMenabled();
     336              :         }
     337              : 
     338          522 :         if (prev && prev->isType(barlineType)) {
     339          515 :             segment = prev;
     340              :         }
     341              :     }
     342              : 
     343          546 :     if (start && !segment->isType(SegmentType::BarLineTypes) && style().styleB(Sid::voltaAlignStartBeforeKeySig)) {
     344            0 :         Segment* prev = segment;
     345            0 :         while (prev && !prev->isType(SegmentType::KeySig) && prev->tick() == segment->tick()) {
     346            0 :             prev = prev->prev1MMenabled();
     347              :         }
     348            0 :         if (prev && prev->isType(SegmentType::KeySig)) {
     349            0 :             segment = prev;
     350              :         }
     351              :     }
     352              : 
     353          546 :     *system = segment->measure()->system();
     354          546 :     double x = segment->x() + segment->measure()->x();
     355              : 
     356          546 :     if (start) {
     357          273 :         bool alignLeftOfRepeatBarLine = false;
     358          273 :         if (segment->isChordRestType()) {
     359           24 :             x -= style().styleAbsolute(Sid::barNoteDistance);
     360          249 :         } else if (segment->isKeySigType()) {
     361            0 :             KeySig* sig = toKeySig(segment->element(track()));
     362            0 :             if (sig && !sig->ldata()->keySymbols.empty()) {
     363            0 :                 KeySym keySym = sig->ldata()->keySymbols.front();
     364            0 :                 PointF cutoutNW = score()->engravingFont()->smuflAnchor(keySym.sym, SmuflAnchorId::cutOutNW, 1.0);
     365            0 :                 x += cutoutNW.x();
     366              :             }
     367          249 :         } else if (segment->segmentType() & SegmentType::BarLineTypes && !isAtSystemStart) {
     368          249 :             x += segment->width();
     369          249 :             const BarLine* barline = toBarLine(segment->element(track()));
     370          249 :             alignLeftOfRepeatBarLine = barline && barline->barLineType() == BarLineType::END_REPEAT
     371          498 :                                        && style().styleB(Sid::voltaAlignEndLeftOfBarline);
     372          249 :             if (alignLeftOfRepeatBarLine) {
     373            0 :                 x -= style().styleAbsolute(Sid::endBarWidth);
     374              :             }
     375              :         }
     376          273 :         x += (isAtSystemStart || alignLeftOfRepeatBarLine ? 0.5 : -0.5) * absoluteFromSpatium(lineWidth());
     377              :     } else {
     378          273 :         if ((*system) && segment->tick() == (*system)->endTick()) {
     379           50 :             staff_idx_t si = backSegment()->effectiveStaffIdx();
     380           50 :             if (si == muse::nidx) {
     381            0 :                 return PointF(x, 0.0);
     382              :             }
     383           50 :             x += segment->staffShape(si).right();
     384           50 :             x -= 0.5 * absoluteFromSpatium(lineWidth());
     385          223 :         } else if (segment->segmentType() & SegmentType::BarLineTypes) {
     386          223 :             BarLine* barLine = toBarLine(segment->element(track()));
     387          223 :             if (barLine->barLineType() == BarLineType::END_REPEAT || barLine->barLineType() == BarLineType::END_START_REPEAT) {
     388          140 :                 x += symWidth(SymId::repeatDot) + style().styleAbsolute(Sid::repeatBarlineDotSeparation);
     389              :             }
     390          223 :             x += 0.5 * absoluteFromSpatium(lineWidth());
     391              :         }
     392              :     }
     393              : 
     394          546 :     return PointF(x, 0.0);
     395              : }
     396              : 
     397              : //---------------------------------------------------------
     398              : //   setVoltaType
     399              : //    deprecated
     400              : //---------------------------------------------------------
     401              : 
     402           33 : void Volta::setVoltaType(Type val)
     403              : {
     404           33 :     setEndHookType(Type::CLOSED == val ? HookType::HOOK_90 : HookType::NONE);
     405           33 : }
     406              : 
     407              : //---------------------------------------------------------
     408              : //   voltaType
     409              : //    deprecated
     410              : //---------------------------------------------------------
     411              : 
     412           22 : Volta::Type Volta::voltaType() const
     413              : {
     414           22 :     return endHookType() != HookType::NONE ? Type::CLOSED : Type::OPEN;
     415              : }
     416              : 
     417          369 : Sid Volta::defaultPosSid() const
     418              : {
     419          369 :     return Sid::voltaPosAbove;
     420              : }
     421              : }
        

Generated by: LCOV version 2.0-1