LCOV - code coverage report
Current view: top level - src/engraving/dom - pedal.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 93.8 % 129 121
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 "pedal.h"
      24              : 
      25              : #include "chord.h"
      26              : #include "chordrest.h"
      27              : #include "measure.h"
      28              : #include "note.h"
      29              : #include "score.h"
      30              : #include "system.h"
      31              : #include "text.h"
      32              : #include "rest.h"
      33              : 
      34              : #include "log.h"
      35              : 
      36              : using namespace mu;
      37              : 
      38              : namespace mu::engraving {
      39              : static const ElementStyle pedalStyle {
      40              :     { Sid::pedalText,                          Pid::BEGIN_TEXT },
      41              :     { Sid::pedalContinueText,                  Pid::CONTINUE_TEXT },
      42              :     { Sid::pedalRosetteEndText,                Pid::END_TEXT },
      43              :     { Sid::pedalFontFace,                      Pid::BEGIN_FONT_FACE },
      44              :     { Sid::pedalFontFace,                      Pid::CONTINUE_FONT_FACE },
      45              :     { Sid::pedalFontFace,                      Pid::END_FONT_FACE },
      46              :     { Sid::pedalFontSize,                      Pid::BEGIN_FONT_SIZE },
      47              :     { Sid::pedalFontSize,                      Pid::CONTINUE_FONT_SIZE },
      48              :     { Sid::pedalFontSize,                      Pid::END_FONT_SIZE },
      49              :     { Sid::pedalFontStyle,                     Pid::BEGIN_FONT_STYLE },
      50              :     { Sid::pedalFontStyle,                     Pid::CONTINUE_FONT_STYLE },
      51              :     { Sid::pedalFontStyle,                     Pid::END_FONT_STYLE },
      52              :     { Sid::pedalMusicalSymbolsScale,           Pid::BEGIN_TEXT_MUSICAL_SYMBOLS_SCALE },
      53              :     { Sid::pedalMusicalSymbolsScale,           Pid::CONTINUE_TEXT_MUSICAL_SYMBOLS_SCALE },
      54              :     { Sid::pedalMusicalSymbolsScale,           Pid::END_TEXT_MUSICAL_SYMBOLS_SCALE },
      55              :     { Sid::dummyMusicalSymbolSize,             Pid::BEGIN_TEXT_MUSIC_SYMBOLS_SIZE },
      56              :     { Sid::dummyMusicalSymbolSize,             Pid::CONTINUE_TEXT_MUSIC_SYMBOLS_SIZE },
      57              :     { Sid::dummyMusicalSymbolSize,             Pid::END_TEXT_MUSIC_SYMBOLS_SIZE },
      58              :     { Sid::pedalTextAlign,                     Pid::BEGIN_TEXT_ALIGN },
      59              :     { Sid::pedalTextAlign,                     Pid::CONTINUE_TEXT_ALIGN },
      60              :     { Sid::pedalTextAlign,                     Pid::END_TEXT_ALIGN },
      61              :     { Sid::pedalPosition,                      Pid::BEGIN_TEXT_POSITION },
      62              :     { Sid::pedalPosition,                      Pid::CONTINUE_TEXT_POSITION },
      63              :     { Sid::pedalPosition,                      Pid::END_TEXT_POSITION },
      64              :     { Sid::pedalHookHeight,                    Pid::BEGIN_HOOK_HEIGHT },
      65              :     { Sid::pedalHookHeight,                    Pid::END_HOOK_HEIGHT },
      66              :     { Sid::pedalLineWidth,                     Pid::LINE_WIDTH },
      67              :     { Sid::pedalDashLineLen,                   Pid::DASH_LINE_LEN },
      68              :     { Sid::pedalDashGapLen,                    Pid::DASH_GAP_LEN },
      69              :     { Sid::pedalPlacement,                     Pid::PLACEMENT },
      70              :     { Sid::pedalLineStyle,                     Pid::LINE_STYLE },
      71              :     { Sid::pedalFontSpatiumDependent,          Pid::TEXT_SIZE_SPATIUM_DEPENDENT },
      72              :     { Sid::pedalEndLineArrowHeight,            Pid::END_LINE_ARROW_HEIGHT },
      73              :     { Sid::pedalEndLineArrowWidth,             Pid::END_LINE_ARROW_WIDTH },
      74              :     { Sid::pedalBeginLineArrowHeight,          Pid::BEGIN_LINE_ARROW_HEIGHT },
      75              :     { Sid::pedalBeginLineArrowWidth,           Pid::BEGIN_LINE_ARROW_WIDTH },
      76              :     { Sid::pedalEndFilledArrowHeight,          Pid::END_FILLED_ARROW_HEIGHT },
      77              :     { Sid::pedalEndFilledArrowWidth,           Pid::END_FILLED_ARROW_WIDTH },
      78              :     { Sid::pedalBeginFilledArrowHeight,        Pid::BEGIN_FILLED_ARROW_HEIGHT },
      79              :     { Sid::pedalBeginFilledArrowWidth,         Pid::BEGIN_FILLED_ARROW_WIDTH },
      80              : };
      81              : 
      82              : const String Pedal::PEDAL_SYMBOL = u"<sym>keyboardPedalPed</sym>";
      83              : const String Pedal::STAR_SYMBOL = u"<sym>keyboardPedalUp</sym>";
      84              : 
      85           74 : PedalSegment::PedalSegment(Pedal* sp)
      86           74 :     : TextLineBaseSegment(ElementType::PEDAL_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      87              : {
      88           74 :     m_text->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      89           74 :     m_endText->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
      90           74 : }
      91              : 
      92         7248 : Sid Pedal::getPropertyStyle(Pid pid) const
      93              : {
      94         7248 :     switch (pid) {
      95          483 :     case Pid::END_TEXT:
      96          483 :         return lineVisible() ? Sid::pedalEndText : Sid::pedalRosetteEndText;
      97          352 :     case Pid::BEGIN_TEXT:
      98          352 :         return beginHookType() == HookType::NONE ? Sid::pedalText : Sid::pedalHookText;
      99          327 :     case Pid::CONTINUE_TEXT:
     100          327 :         return beginHookType() == HookType::NONE ? Sid::pedalContinueText : Sid:: pedalContinueHookText;
     101         6086 :     default:
     102         6086 :         return TextLineBase::getPropertyStyle(pid);
     103              :     }
     104              : }
     105              : 
     106              : //---------------------------------------------------------
     107              : //   Pedal
     108              : //---------------------------------------------------------
     109              : 
     110          155 : Pedal::Pedal(DummyParentOr<EngravingItem> parent)
     111          155 :     : TextLineBase(ElementType::PEDAL, parent)
     112              : {
     113          155 :     initElementStyle(&pedalStyle);
     114          155 :     setLineVisible(true);
     115              : 
     116          155 :     resetProperty(Pid::LINE_WIDTH);
     117          155 :     resetProperty(Pid::LINE_STYLE);
     118              : 
     119          155 :     resetProperty(Pid::BEGIN_HOOK_TYPE);
     120          155 :     resetProperty(Pid::END_HOOK_TYPE);
     121              : 
     122          155 :     resetProperty(Pid::BEGIN_TEXT_PLACE);
     123          155 :     resetProperty(Pid::LINE_VISIBLE);
     124          155 :     resetProperty(Pid::END_TEXT);
     125          155 : }
     126              : 
     127              : //---------------------------------------------------------
     128              : //   createLineSegment
     129              : //---------------------------------------------------------
     130              : 
     131              : static const ElementStyle pedalSegmentStyle {
     132              :     { Sid::pedalMinDistance, Pid::MIN_DISTANCE },
     133              : };
     134              : 
     135           74 : LineSegment* Pedal::createLineSegment()
     136              : {
     137           74 :     PedalSegment* p = new PedalSegment(this);
     138           74 :     p->setTrack(track());
     139           74 :     p->initElementStyle(&pedalSegmentStyle);
     140           74 :     return p;
     141              : }
     142              : 
     143              : //---------------------------------------------------------
     144              : //   propertyDefault
     145              : //---------------------------------------------------------
     146              : 
     147         3213 : engraving::PropertyValue Pedal::propertyDefault(Pid propertyId) const
     148              : {
     149         3213 :     switch (propertyId) {
     150          158 :     case Pid::LINE_WIDTH:
     151          158 :         return style().styleS(Sid::pedalLineWidth);              // return point, not spatium
     152              : 
     153          158 :     case Pid::LINE_STYLE:
     154          158 :         return style().styleV(Sid::pedalLineStyle);
     155              : 
     156          687 :     case Pid::BEGIN_TEXT:
     157              :     case Pid::CONTINUE_TEXT:
     158              :     case Pid::END_TEXT:
     159          687 :         return style().styleV(getPropertyStyle(propertyId));
     160              : 
     161          344 :     case Pid::BEGIN_TEXT_PLACE:
     162              :     case Pid::CONTINUE_TEXT_PLACE:
     163              :     case Pid::END_TEXT_PLACE:
     164          344 :         return TextPlace::LEFT;
     165              : 
     166          189 :     case Pid::BEGIN_TEXT_OFFSET:
     167              :     case Pid::CONTINUE_TEXT_OFFSET:
     168              :     case Pid::END_TEXT_OFFSET:
     169          189 :         return PropertyValue::fromValue(PointF(0, 0));
     170              : 
     171          436 :     case Pid::BEGIN_HOOK_TYPE:
     172              :     case Pid::END_HOOK_TYPE:
     173          436 :         return HookType::NONE;
     174              : 
     175          218 :     case Pid::LINE_VISIBLE:
     176          218 :         return true;
     177              : 
     178            9 :     case Pid::PLACEMENT:
     179            9 :         return style().styleV(Sid::pedalPlacement);
     180              : 
     181          148 :     case Pid::TEXT_STYLE:
     182          148 :         return TextStyleType::PEDAL;
     183              : 
     184          866 :     default:
     185          866 :         return TextLineBase::propertyDefault(propertyId);
     186              :     }
     187              : }
     188              : 
     189         7753 : bool Pedal::setProperty(Pid propertyId, const PropertyValue& v)
     190              : {
     191              :     // Update style flag for text
     192         7753 :     if (propertyId == Pid::BEGIN_HOOK_TYPE) {
     193          158 :         setBeginHookType(v.value<HookType>());
     194              : 
     195          316 :         PropertyFlags beginTextStyleFlag = beginText() == propertyDefault(Pid::BEGIN_TEXT).value<String>()
     196          158 :                                            ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
     197          158 :         setPropertyFlags(Pid::BEGIN_TEXT, beginTextStyleFlag);
     198          316 :         PropertyFlags continueTextStyleFlag = continueText() == propertyDefault(Pid::CONTINUE_TEXT).value<String>()
     199          158 :                                               ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
     200          158 :         setPropertyFlags(Pid::CONTINUE_TEXT, continueTextStyleFlag);
     201         7595 :     } else if (propertyId == Pid::LINE_VISIBLE) {
     202          158 :         setLineVisible(v.toBool());
     203          316 :         PropertyFlags endTextStyleFlag = endText() == propertyDefault(Pid::END_TEXT).value<String>()
     204          158 :                                          ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
     205          158 :         setPropertyFlags(Pid::END_TEXT, endTextStyleFlag);
     206              :     } else {
     207         7437 :         return TextLineBase::setProperty(propertyId, v);
     208              :     }
     209              : 
     210          316 :     triggerLayout();
     211          316 :     return true;
     212              : }
     213              : 
     214          115 : Pedal* Pedal::findNextInStaff() const
     215              : {
     216          115 :     Fraction endTick = tick2();
     217          115 :     auto spanners = score()->spannerMap().findOverlapping(endTick.ticks(), score()->endTick().ticks());
     218          831 :     for (auto element : spanners) {
     219          733 :         Spanner* spanner = element.value;
     220          733 :         if (spanner->isPedal() && spanner != this && spanner->staffIdx() == staffIdx() && spanner->tick() == endTick) {
     221           17 :             return toPedal(spanner);
     222              :         }
     223              :     }
     224              : 
     225           98 :     return nullptr;
     226          115 : }
     227              : 
     228           24 : bool Pedal::connect45HookToNext() const
     229              : {
     230           24 :     if (endHookType() != HookType::HOOK_45) {
     231           24 :         return false;
     232              :     }
     233              : 
     234            0 :     Pedal* nextPedal = findNextInStaff();
     235              : 
     236            0 :     return nextPedal && nextPedal->tick() == tick2() && nextPedal->beginHookType() == HookType::HOOK_45;
     237              : }
     238              : 
     239              : //---------------------------------------------------------
     240              : //   linePos
     241              : //    return System() coordinates
     242              : //---------------------------------------------------------
     243              : 
     244          230 : PointF Pedal::linePos(Grip grip, System** sys) const
     245              : {
     246          230 :     bool start = grip == Grip::START;
     247              : 
     248          230 :     if (start) {
     249          115 :         Segment* startSeg = startSegment();
     250          115 :         if (!startSeg) {
     251            0 :             return PointF();
     252              :         }
     253          115 :         *sys = startSeg->measure()->system();
     254          115 :         double x = startSeg->x() + startSeg->measure()->x();
     255          115 :         if (beginText() == "<sym>keyboardPedalPed</sym>") {
     256           85 :             x -= 0.5 * spatium();
     257           30 :         } else if (beginHookType() == HookType::HOOK_90 || beginHookType() == HookType::HOOK_90T) {
     258           17 :             x += 0.5 * absoluteFromSpatium(lineWidth());
     259           13 :         } else if (beginHookType() == HookType::HOOK_45) {
     260            8 :             EngravingItem* item = startElement();
     261            8 :             if (item && item->isChord()) {
     262            8 :                 Note* downNote = toChord(item)->downNote();
     263            8 :                 x += 0.5 * downNote->headWidth();
     264            0 :             } else if (item && item->isRest()) {
     265            0 :                 x += toRest(item)->centerX();
     266              :             }
     267              :         }
     268          115 :         return PointF(x, 0.0);
     269              :     }
     270              : 
     271          115 :     Segment* endSeg = endSegment();
     272          115 :     if (!endSeg) {
     273            0 :         return PointF();
     274              :     }
     275              : 
     276          115 :     Pedal* nextPedal = findNextInStaff();
     277              : 
     278          115 :     if (nextPedal && endHookType() == HookType::HOOK_45) {
     279            8 :         *sys = endSeg->measure()->system();
     280            8 :         double x = endSeg->x() + endSeg->measure()->x();
     281            8 :         EngravingItem* item = endElement();
     282            8 :         if (item && item->isChord()) {
     283            8 :             Note* downNote = toChord(item)->downNote();
     284            8 :             x += 0.5 * downNote->headWidth();
     285            0 :         } else if (item && item->isRest()) {
     286            0 :             x += toRest(item)->centerX();
     287              :         }
     288            8 :         return PointF(x, 0.0);
     289              :     }
     290              : 
     291          107 :     if (endSeg->rtick() == Fraction(0, 1)) {
     292           56 :         Segment* prevSeg = endSeg->prev1(SegmentType::EndBarLine);
     293           56 :         if (prevSeg) {
     294           56 :             endSeg = prevSeg;
     295              :         }
     296              :     }
     297              : 
     298          107 :     *sys = endSeg->measure()->system();
     299          107 :     double x = endSeg->x() + endSeg->measure()->x();
     300              : 
     301          107 :     x -= (endSeg->isChordRestType() && nextPedal ? 1.25 : 0.75) * spatium();
     302              : 
     303          107 :     return PointF(x, 0.0);
     304              : }
     305              : 
     306          122 : Sid Pedal::defaultPosSid() const
     307              : {
     308          122 :     return placeAbove() ? Sid::pedalPosAbove : Sid::pedalPosBelow;
     309              : }
     310              : }
        

Generated by: LCOV version 2.0-1