LCOV - code coverage report
Current view: top level - src/engraving/dom - trill.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 74.6 % 213 159
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 "trill.h"
      24              : 
      25              : #include <cmath>
      26              : 
      27              : #include "../editing/addremoveelement.h"
      28              : #include "types/typesconv.h"
      29              : 
      30              : #include "iengravingfont.h"
      31              : 
      32              : #include "accidental.h"
      33              : #include "chord.h"
      34              : #include "clef.h"
      35              : #include "factory.h"
      36              : #include "measure.h"
      37              : #include "ornament.h"
      38              : #include "score.h"
      39              : #include "system.h"
      40              : 
      41              : using namespace mu::engraving;
      42              : 
      43              : //---------------------------------------------------------
      44              : //   trillStyle
      45              : //---------------------------------------------------------
      46              : 
      47              : static const ElementStyle trillStyle {
      48              :     { Sid::trillPlacement, Pid::PLACEMENT },
      49              : };
      50              : 
      51           63 : TrillSegment::TrillSegment(Trill* sp)
      52           63 :     : LineSegment(ElementType::TRILL_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      53              : {
      54           63 : }
      55              : 
      56              : //---------------------------------------------------------
      57              : //   remove
      58              : //---------------------------------------------------------
      59              : 
      60            0 : void TrillSegment::remove(EngravingItem* e)
      61              : {
      62            0 :     if (trill()->accidental() == e) {
      63              :         // accidental is part of trill
      64            0 :         trill()->setAccidental(nullptr);
      65            0 :         e->removed();
      66              :     }
      67            0 : }
      68              : 
      69              : //---------------------------------------------------------
      70              : //   symbolLine
      71              : //---------------------------------------------------------
      72              : 
      73          108 : void TrillSegment::symbolLine(SymId start, SymId fill)
      74              : {
      75          108 :     double x1 = 0.0;
      76          108 :     double x2 = pos2().x();
      77          108 :     double w   = x2 - x1;
      78          108 :     double mag = magS();
      79          108 :     IEngravingFontPtr f = score()->engravingFont();
      80              : 
      81          108 :     m_symbols.clear();
      82          108 :     m_symbols.push_back(start);
      83          108 :     double w1 = f->advance(start, mag);
      84          108 :     double w2 = f->advance(fill, mag);
      85          108 :     int n    = lrint((w - w1) / w2);
      86         1491 :     for (int i = 0; i < n; ++i) {
      87         1383 :         m_symbols.push_back(fill);
      88              :     }
      89          108 :     RectF r(f->bbox(m_symbols, mag));
      90          108 :     setbbox(r);
      91          108 : }
      92              : 
      93            8 : void TrillSegment::symbolLine(SymId start, SymId fill, SymId end)
      94              : {
      95            8 :     double x1 = 0.0;
      96            8 :     double x2 = pos2().x();
      97            8 :     double w   = x2 - x1;
      98            8 :     double mag = magS();
      99            8 :     IEngravingFontPtr f = score()->engravingFont();
     100              : 
     101            8 :     m_symbols.clear();
     102            8 :     m_symbols.push_back(start);
     103            8 :     double w1 = f->advance(start, mag);
     104            8 :     double w2 = f->advance(fill, mag);
     105            8 :     double w3 = f->advance(end, mag);
     106            8 :     int n    = lrint((w - w1 - w3) / w2);
     107          104 :     for (int i = 0; i < n; ++i) {
     108           96 :         m_symbols.push_back(fill);
     109              :     }
     110            8 :     m_symbols.push_back(end);
     111            8 :     RectF r(f->bbox(m_symbols, mag));
     112            8 :     setbbox(r);
     113            8 : }
     114              : 
     115            0 : void TrillSegment::rebaseAnchors(EditData& ed, Grip grip)
     116              : {
     117            0 :     EngravingItem* startElement = spanner()->startElement();
     118            0 :     if (startElement && startElement->isChord() && toChord(startElement)->staffMove() != 0) {
     119              :         // This trill is on a cross-staff chord. Don't try to rebase its anchors when dragging.
     120            0 :         return;
     121              :     }
     122              : 
     123            0 :     LineSegment::rebaseAnchors(ed, grip);
     124              : }
     125              : 
     126              : //---------------------------------------------------------
     127              : //   scanElements
     128              : //---------------------------------------------------------
     129              : 
     130            1 : void TrillSegment::scanElements(std::function<void(EngravingItem*)> func)
     131              : {
     132            1 :     func(this);
     133            1 :     if (isSingleType() || isBeginType()) {
     134            1 :         Accidental* a = trill()->accidental();
     135            1 :         if (a) {
     136            0 :             func(a);
     137              :         }
     138            1 :         Chord* cueNoteChord = trill()->cueNoteChord();
     139            1 :         if (cueNoteChord) {
     140            0 :             cueNoteChord->scanElements(func);
     141              :         }
     142              :     }
     143            1 : }
     144              : 
     145              : //---------------------------------------------------------
     146              : //   propertyDelegate
     147              : //---------------------------------------------------------
     148              : 
     149          195 : EngravingObject* TrillSegment::propertyDelegate(Pid pid) const
     150              : {
     151          195 :     if (pid == Pid::TRILL_TYPE || pid == Pid::ORNAMENT_STYLE || pid == Pid::PLACEMENT) {
     152            0 :         return spanner();
     153              :     }
     154          195 :     return LineSegment::propertyDelegate(pid);
     155              : }
     156              : 
     157              : //---------------------------------------------------------
     158              : //   Trill
     159              : //---------------------------------------------------------
     160              : 
     161           67 : Trill::Trill(DummyParentOr<EngravingItem> parent)
     162           67 :     : SLine(ElementType::TRILL, parent)
     163              : {
     164           67 :     m_trillType     = TrillType::TRILL_LINE;
     165           67 :     m_ornament = nullptr;
     166           67 :     m_accidental = nullptr;
     167           67 :     m_cueNoteChord = nullptr;
     168           67 :     m_ornamentStyle = OrnamentStyle::DEFAULT;
     169           67 :     initElementStyle(&trillStyle);
     170           67 : }
     171              : 
     172            2 : Trill::Trill(const Trill& t)
     173            2 :     : SLine(t)
     174              : {
     175            2 :     m_trillType = t.m_trillType;
     176            2 :     m_ornament = t.m_ornament ? t.m_ornament->clone() : nullptr;
     177            2 :     m_ornamentStyle = t.m_ornamentStyle;
     178            2 :     initElementStyle(&trillStyle);
     179            2 : }
     180              : 
     181            2 : EngravingItem* Trill::linkedClone()
     182              : {
     183            2 :     Trill* linkedTrill = clone();
     184            2 :     Ornament* linkedOrnament = toOrnament(m_ornament->linkedClone());
     185            2 :     linkedTrill->setOrnament(linkedOrnament);
     186            2 :     linkedTrill->setAutoplace(true);
     187            2 :     score()->undo(new Link(linkedTrill, this));
     188            2 :     return linkedTrill;
     189              : }
     190              : 
     191              : //---------------------------------------------------------
     192              : //   remove
     193              : //---------------------------------------------------------
     194              : 
     195            0 : void Trill::remove(EngravingItem* e)
     196              : {
     197            0 :     if (e == m_accidental) {
     198            0 :         m_accidental = nullptr;
     199            0 :         e->removed();
     200              :     }
     201            0 : }
     202              : 
     203          115 : void Trill::setTrack(track_idx_t n)
     204              : {
     205          115 :     EngravingItem::setTrack(n);
     206              : 
     207          118 :     for (SpannerSegment* ss : spannerSegments()) {
     208            3 :         ss->setTrack(n);
     209              :     }
     210              : 
     211          115 :     if (m_ornament) {
     212           54 :         m_ornament->setTrack(n);
     213              :     }
     214          115 : }
     215              : 
     216            2 : void Trill::setScore(Score* s)
     217              : {
     218            2 :     Spanner::setScore(s);
     219            2 :     if (m_ornament) {
     220            2 :         m_ornament->setScore(s);
     221              :     }
     222            2 : }
     223              : 
     224          212 : void Trill::computeStartElement()
     225              : {
     226          212 :     Spanner::computeStartElement();
     227          212 :     if (startElement() && startElement()->isChord() && m_ornament) {
     228          194 :         m_ornament->setOwnershipParent(startElement());
     229              : 
     230          194 :         Chord* cueChord = m_ornament->cueNoteChord();
     231          194 :         if (cueChord) {
     232           13 :             cueChord->setOwnershipParent(toChord(startElement())->segment());
     233              :         }
     234              :     }
     235          212 : }
     236              : 
     237          286 : PointF Trill::trillLinePos(const SLine* line, Grip grip, System** system)
     238              : {
     239          286 :     if (!line) {
     240            0 :         return PointF();
     241              :     }
     242              : 
     243          286 :     bool start = grip == Grip::START;
     244          286 :     bool mmRest = line->style().styleB(Sid::createMultiMeasureRests);
     245          286 :     double graceOffset = 0.0;
     246          286 :     double clefOffset = 0.0;
     247              : 
     248          286 :     Segment* segment = start ? line->startSegment() : line->endSegment();
     249          286 :     if (!segment) {
     250            0 :         return PointF();
     251              :     }
     252              : 
     253          286 :     if (start) {
     254          143 :         *system = segment->measure()->system();
     255          143 :         double x = segment->x() + segment->measure()->x();
     256          143 :         return PointF(x, 0.0);
     257              :     }
     258              : 
     259          143 :     Segment* clefSeg = segment->isClefType() ? segment : nullptr;
     260          143 :     Fraction curTick = segment->tick();
     261              :     while (true) {
     262          219 :         Segment* prevSeg = mmRest ? segment->prev1MM() : segment->prev1();
     263          219 :         if (prevSeg && prevSeg->tick() == curTick) {
     264           76 :             clefSeg = prevSeg->isClefType() ? prevSeg : clefSeg;
     265           76 :             segment = prevSeg;
     266              :         } else {
     267          143 :             break;
     268              :         }
     269           76 :     }
     270              : 
     271              :     // Stop line before clefs
     272          143 :     if (clefSeg) {
     273            0 :         EngravingItem* clefItem = clefSeg->element(line->track2());
     274            0 :         if (clefItem && clefItem->isClef()) {
     275            0 :             Clef* clef = toClef(clefItem);
     276            0 :             SymId clefSym = ClefInfo::symId(clef->clefType());
     277            0 :             Shape clefShape = line->symShapeWithCutouts(clefSym).translated(clef->pos());
     278            0 :             clefOffset = segment->pageX() - clef->pageX() + clefShape.leftMostEdgeAtTop();
     279            0 :         }
     280              :     }
     281              : 
     282              :     // Stop line before grace notes
     283          143 :     if (const EngravingItem* preAppendedItem = segment->preAppendedItem(line->track2());
     284          143 :         preAppendedItem&& preAppendedItem->isGraceNotesGroup()) {
     285              :         // get x position of leftmost grace note
     286            0 :         const Chord* leftMostGraceChord = nullptr;
     287            0 :         const GraceNotesGroup* graceGroup = toGraceNotesGroup(preAppendedItem);
     288            0 :         for (const Chord* graceChord : *graceGroup) {
     289            0 :             leftMostGraceChord = leftMostGraceChord
     290            0 :                                  && leftMostGraceChord->x() < graceChord->x() ? leftMostGraceChord : graceChord;
     291              :         }
     292            0 :         if (leftMostGraceChord) {
     293            0 :             graceOffset = segment->pageX() - leftMostGraceChord->pageX();
     294              :         }
     295              :     }
     296              : 
     297          143 :     double offset = std::max(graceOffset, clefOffset);
     298              : 
     299          143 :     *system = segment->measure()->system();
     300          143 :     double x = segment->x() + segment->measure()->x() - line->spatium() - offset;
     301          143 :     return PointF(x, 0.0);
     302              : }
     303              : 
     304          230 : PointF Trill::linePos(Grip grip, System** system) const
     305              : {
     306          230 :     return trillLinePos(this, grip, system);
     307              : }
     308              : 
     309           53 : void Trill::setTrillType(TrillType tt)
     310              : {
     311           53 :     m_trillType = tt;
     312           53 :     if (!m_ornament) {
     313              :         // ornament parent will be explicitely set at layout stage
     314           53 :         m_ornament = Factory::createOrnament(score()->dummy());
     315              :     }
     316           53 :     m_ornament->setTrack(track());
     317           53 :     m_ornament->setSymId(Ornament::fromTrillType(tt));
     318           53 : }
     319              : 
     320              : //---------------------------------------------------------
     321              : //   createLineSegment
     322              : //---------------------------------------------------------
     323              : 
     324              : static const ElementStyle trillSegmentStyle {
     325              :     { Sid::trillMinDistance, Pid::MIN_DISTANCE },
     326              : };
     327              : 
     328           63 : LineSegment* Trill::createLineSegment()
     329              : {
     330           63 :     TrillSegment* seg = new TrillSegment(this);
     331           63 :     seg->setTrack(track());
     332           63 :     seg->setColor(lineColor());
     333           63 :     seg->initElementStyle(&trillSegmentStyle);
     334           63 :     return seg;
     335              : }
     336              : 
     337              : //---------------------------------------------------------
     338              : //   trillTypeName
     339              : //---------------------------------------------------------
     340              : 
     341            0 : String Trill::trillTypeUserName() const
     342              : {
     343            0 :     return TConv::translatedUserName(trillType());
     344              : }
     345              : 
     346              : //---------------------------------------------------------
     347              : //   getProperty
     348              : //---------------------------------------------------------
     349              : 
     350          259 : PropertyValue Trill::getProperty(Pid propertyId) const
     351              : {
     352          259 :     switch (propertyId) {
     353            0 :     case Pid::TRILL_TYPE:
     354            0 :         return int(trillType());
     355           18 :     case Pid::ORNAMENT_STYLE:
     356           18 :         return ornamentStyle();
     357          241 :     default:
     358          241 :         break;
     359              :     }
     360          241 :     return SLine::getProperty(propertyId);
     361              : }
     362              : 
     363              : //---------------------------------------------------------
     364              : //   setProperty
     365              : //---------------------------------------------------------
     366              : 
     367          110 : bool Trill::setProperty(Pid propertyId, const PropertyValue& val)
     368              : {
     369          110 :     switch (propertyId) {
     370            0 :     case Pid::TRILL_TYPE:
     371            0 :         setTrillType(TrillType(val.toInt()));
     372            0 :         break;
     373            0 :     case Pid::ORNAMENT_STYLE:
     374            0 :         setOrnamentStyle(val.value<OrnamentStyle>());
     375            0 :         break;
     376            0 :     case Pid::COLOR:
     377            0 :         setColor(val.value<Color>());
     378            0 :         setLineColor(val.value<Color>());
     379            0 :         break;
     380          110 :     default:
     381          110 :         return SLine::setProperty(propertyId, val);
     382              :     }
     383            0 :     triggerLayout();
     384            0 :     return true;
     385              : }
     386              : 
     387              : //---------------------------------------------------------
     388              : //   propertyDefault
     389              : //---------------------------------------------------------
     390              : 
     391          255 : PropertyValue Trill::propertyDefault(Pid propertyId) const
     392              : {
     393          255 :     switch (propertyId) {
     394            0 :     case Pid::TRILL_TYPE:
     395            0 :         return static_cast<int>(TrillType::TRILL_LINE);
     396           18 :     case Pid::ORNAMENT_STYLE:
     397           18 :         return OrnamentStyle::DEFAULT;
     398            2 :     case Pid::PLACEMENT:
     399            2 :         return style().styleV(Sid::trillPlacement);
     400              : 
     401          235 :     default:
     402          235 :         return SLine::propertyDefault(propertyId);
     403              :     }
     404              : }
     405              : 
     406              : //---------------------------------------------------------
     407              : //   subtypeUserName
     408              : //---------------------------------------------------------
     409              : 
     410            1 : muse::TranslatableString Trill::subtypeUserName() const
     411              : {
     412            1 :     return TConv::userName(trillType());
     413              : }
     414              : 
     415              : //---------------------------------------------------------
     416              : //   accessibleInfo
     417              : //---------------------------------------------------------
     418              : 
     419            0 : String Trill::accessibleInfo() const
     420              : {
     421            0 :     return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), trillTypeUserName());
     422              : }
     423              : 
     424          118 : Sid Trill::defaultPosSid() const
     425              : {
     426          118 :     return placeAbove() ? Sid::trillPosAbove : Sid::trillPosBelow;
     427              : }
     428              : 
     429          211 : void Trill::doComputeEndElement()
     430              : {
     431          211 :     setEndElement(score()->findChordRestEndingBeforeTickInStaffAndVoice(tick2(), track2staff(track2()), voice()));
     432          211 : }
        

Generated by: LCOV version 2.0-1