LCOV - code coverage report
Current view: top level - src/engraving/dom - spanner.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 70.6 % 773 546
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              : #include "spanner.h"
      23              : 
      24              : #include "translation.h"
      25              : 
      26              : #include "../editing/navigation.h"
      27              : 
      28              : #include "anchors.h"
      29              : #include "chord.h"
      30              : #include "chordrest.h"
      31              : #include "location.h"
      32              : #include "lyrics.h"
      33              : #include "measure.h"
      34              : #include "note.h"
      35              : #include "part.h"
      36              : #include "score.h"
      37              : #include "segment.h"
      38              : #include "staff.h"
      39              : #include "system.h"
      40              : #include "types/typesconv.h"
      41              : 
      42              : #include "log.h"
      43              : 
      44              : using namespace mu;
      45              : using namespace mu::engraving;
      46              : 
      47              : namespace mu::engraving {
      48              : //---------------------------------------------------------
      49              : //   SpannerSegment
      50              : //---------------------------------------------------------
      51              : 
      52         4193 : SpannerSegment::SpannerSegment(const ElementType& type, Spanner* sp, ElementFlags f)
      53         4193 :     : EngravingItem(type, sp, f)
      54              : {
      55         4193 :     setSpannerSegmentType(SpannerSegmentType::SINGLE);
      56         4193 : }
      57              : 
      58          589 : SpannerSegment::SpannerSegment(const SpannerSegment& s)
      59          589 :     : EngravingItem(s)
      60              : {
      61          589 :     m_spannerSegmentType = s.m_spannerSegmentType;
      62          589 :     m_p2                 = s.m_p2;
      63          589 :     m_offset2            = s.m_offset2;
      64          589 : }
      65              : 
      66         4067 : SpannerSegment::~SpannerSegment()
      67              : {
      68              :     // A system only places a segment, so it holds a plain back-reference to it: take
      69              :     // that reference with us, rather than relying on every deletion site to do it.
      70         4067 :     moveToSystem(nullptr);
      71         4067 : }
      72              : 
      73         7532 : void SpannerSegment::setSpanner(Spanner* val)
      74              : {
      75         7532 :     setOwnershipParent(val);
      76         7532 : }
      77              : 
      78         7532 : void SpannerSegment::setOwnershipParent(Spanner* spanner)
      79              : {
      80         7532 :     EngravingItem::setOwnershipParent(spanner);
      81         7532 : }
      82              : 
      83        63246 : EngravingItem* SpannerSegment::layoutParent() const
      84              : {
      85        63246 :     return m_system;
      86              : }
      87              : 
      88           25 : EngravingItem* SpannerSegment::accessibleParentItem() const
      89              : {
      90              :     // Not placed on a system yet, so it is the spanner that lists it;
      91              :     // see Spanner::accessibleChildren().
      92           25 :     if (!m_system) {
      93            1 :         if (EngravingItem* owner = ownershipParentItem()) {
      94            1 :             return owner;
      95              :         }
      96              :     }
      97              : 
      98           24 :     return EngravingItem::accessibleParentItem();
      99              : }
     100              : 
     101              : //---------------------------------------------------------
     102              : //   mag
     103              : //---------------------------------------------------------
     104              : 
     105        53133 : double SpannerSegment::mag() const
     106              : {
     107        53133 :     if (spanner()->systemFlag()) {
     108          756 :         return 1.0;
     109              :     }
     110        52377 :     return staff() ? staff()->staffMag(spanner()->tick()) : 1.0;
     111              : }
     112              : 
     113        17783 : Fraction SpannerSegment::tick() const
     114              : {
     115        17783 :     const Spanner* sp = spanner();
     116        17783 :     return sp ? sp->tick() : Fraction(0, 1);
     117              : }
     118              : 
     119              : //---------------------------------------------------------
     120              : //   moveToSystem
     121              : //---------------------------------------------------------
     122              : 
     123        15207 : void SpannerSegment::moveToSystem(System* s)
     124              : {
     125        15207 :     if (m_system == s) {
     126         7590 :         return;
     127              :     }
     128         7617 :     if (m_system) {
     129         1490 :         m_system->remove(this);
     130              :     }
     131         7617 :     m_system = s;
     132         7617 :     if (s) {
     133         6210 :         s->add(this);
     134              :     }
     135              : }
     136              : 
     137              : //---------------------------------------------------------
     138              : //   spatiumChanged
     139              : //---------------------------------------------------------
     140              : 
     141            0 : void SpannerSegment::spatiumChanged(double ov, double nv)
     142              : {
     143            0 :     EngravingItem::spatiumChanged(ov, nv);
     144            0 :     if (offsetIsSpatiumDependent()) {
     145            0 :         m_offset2 *= (nv / ov);
     146              :     }
     147            0 : }
     148              : 
     149              : //---------------------------------------------------------
     150              : //   mimeData
     151              : //---------------------------------------------------------
     152              : 
     153            0 : muse::ByteArray SpannerSegment::mimeData(const PointF& dragOffset) const
     154              : {
     155            0 :     if (dragOffset.isNull()) { // where is dragOffset used?
     156            0 :         return spanner()->mimeData(dragOffset);
     157              :     }
     158            0 :     return EngravingItem::mimeData(dragOffset);
     159              : }
     160              : 
     161              : //---------------------------------------------------------
     162              : //   propertyDelegate
     163              : //---------------------------------------------------------
     164              : 
     165        15248 : EngravingObject* SpannerSegment::propertyDelegate(Pid pid) const
     166              : {
     167        15248 :     switch (pid) {
     168         2606 :     case Pid::PLAY:
     169              :     case Pid::COLOR:
     170              :     case Pid::Z:
     171              :     case Pid::VISIBLE:
     172              :     case Pid::PLACEMENT:
     173              :     case Pid::EXCLUDE_FROM_OTHER_PARTS:
     174              :     case Pid::POSITION_LINKED_TO_MASTER:
     175              :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     176              :     case Pid::SPANNER_TICK:
     177              :     case Pid::SPANNER_TICKS:
     178              :     case Pid::SPANNER_TRACK2:
     179         2606 :         return spanner();
     180        12642 :     default: break;
     181              :     }
     182              : 
     183        12642 :     return nullptr;
     184              : }
     185              : 
     186              : //---------------------------------------------------------
     187              : //   getProperty
     188              : //---------------------------------------------------------
     189              : 
     190         3440 : engraving::PropertyValue SpannerSegment::getProperty(Pid pid) const
     191              : {
     192         3440 :     if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
     193         1579 :         return e->getProperty(pid);
     194              :     }
     195         1861 :     switch (pid) {
     196            6 :     case Pid::OFFSET2:
     197            6 :         return m_offset2;
     198         1855 :     default:
     199         1855 :         return EngravingItem::getProperty(pid);
     200              :     }
     201              : }
     202              : 
     203              : //---------------------------------------------------------
     204              : //   setProperty
     205              : //---------------------------------------------------------
     206              : 
     207         1465 : bool SpannerSegment::setProperty(Pid pid, const PropertyValue& v)
     208              : {
     209         1465 :     if (EngravingObject* e = propertyDelegate(pid)) {
     210            0 :         return e->setProperty(pid, v);
     211              :     }
     212         1465 :     switch (pid) {
     213            3 :     case Pid::OFFSET2:
     214            3 :         m_offset2 = v.value<PointF>();
     215            3 :         triggerLayout();
     216            3 :         break;
     217         1462 :     default:
     218         1462 :         return EngravingItem::setProperty(pid, v);
     219              :     }
     220            3 :     return true;
     221              : }
     222              : 
     223              : //---------------------------------------------------------
     224              : //   propertyDefault
     225              : //---------------------------------------------------------
     226              : 
     227         3383 : PropertyValue SpannerSegment::propertyDefault(Pid pid) const
     228              : {
     229         3383 :     if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
     230         1566 :         return e->propertyDefault(pid);
     231              :     }
     232         1817 :     switch (pid) {
     233            0 :     case Pid::OFFSET2:
     234            0 :         return PropertyValue();
     235         1817 :     default:
     236         1817 :         return EngravingItem::propertyDefault(pid);
     237              :     }
     238              : }
     239              : 
     240          169 : PointF SpannerSegment::defaultPos() const
     241              : {
     242          169 :     return spanner() ? spanner()->defaultPos() : PointF();
     243              : }
     244              : 
     245              : //---------------------------------------------------------
     246              : //   getPropertyStyle
     247              : //---------------------------------------------------------
     248              : 
     249         2588 : Sid SpannerSegment::getPropertyStyle(Pid pid) const
     250              : {
     251         2588 :     if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
     252            0 :         return e->getPropertyStyle(pid);
     253              :     }
     254         2588 :     return EngravingItem::getPropertyStyle(pid);
     255              : }
     256              : 
     257              : //---------------------------------------------------------
     258              : //   resetProperty
     259              : //---------------------------------------------------------
     260              : 
     261            0 : void SpannerSegment::resetProperty(Pid pid)
     262              : {
     263            0 :     if (EngravingObject* e = propertyDelegate(pid)) {
     264            0 :         return e->resetProperty(pid);
     265              :     }
     266            0 :     return EngravingItem::resetProperty(pid);
     267              : }
     268              : 
     269              : //---------------------------------------------------------
     270              : //   styleChanged
     271              : //---------------------------------------------------------
     272              : 
     273           53 : void SpannerSegment::styleChanged()
     274              : {
     275           53 :     spanner()->styleChanged();
     276           53 : }
     277              : 
     278              : //---------------------------------------------------------
     279              : //   reset
     280              : //---------------------------------------------------------
     281              : 
     282            0 : void SpannerSegment::reset()
     283              : {
     284            0 :     undoChangeProperty(Pid::OFFSET2, PointF());
     285            0 :     EngravingItem::reset();
     286            0 :     spanner()->reset();
     287            0 : }
     288              : 
     289              : //---------------------------------------------------------
     290              : //   undoChangeProperty
     291              : //---------------------------------------------------------
     292              : 
     293           30 : void SpannerSegment::undoChangeProperty(Pid pid, const PropertyValue& val, PropertyFlags ps)
     294              : {
     295           30 :     if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
     296              :         // Switching autoplacement on. Save user-defined
     297              :         // placement properties to undo stack.
     298            3 :         undoPushProperty(Pid::OFFSET2);
     299              :         // other will be saved in EngravingItem::undoChangeProperty
     300              :     }
     301           30 :     EngravingItem::undoChangeProperty(pid, val, ps);
     302           30 : }
     303              : 
     304              : //---------------------------------------------------------
     305              : //   setSelected
     306              : //---------------------------------------------------------
     307              : 
     308         8810 : void SpannerSegment::setSelected(bool f)
     309              : {
     310         8810 :     EngravingItem::setSelected(f);
     311         8810 :     if (spanner()->selected() != f) {
     312         3792 :         spanner()->setSelected(f);
     313              :     }
     314         8810 : }
     315              : 
     316              : //---------------------------------------------------------
     317              : //   setVisible
     318              : //---------------------------------------------------------
     319              : 
     320         2658 : void SpannerSegment::setVisible(bool f)
     321              : {
     322         2658 :     if (Spanner* sp = spanner()) {
     323         2658 :         sp->setVisible(f);
     324              :     }
     325         2658 :     EngravingItem::setVisible(f);
     326         2658 : }
     327              : 
     328              : //---------------------------------------------------------
     329              : //   setColor
     330              : //---------------------------------------------------------
     331              : 
     332          900 : void SpannerSegment::setColor(const Color& col)
     333              : {
     334          900 :     if (Spanner* sp = spanner()) {
     335          900 :         sp->setColor(col);
     336              :     }
     337          900 :     EngravingItem::setColor(col);
     338          900 : }
     339              : 
     340              : //---------------------------------------------------------
     341              : //   setZ
     342              : //---------------------------------------------------------
     343              : 
     344            0 : void SpannerSegment::setZ(int val)
     345              : {
     346            0 :     if (Spanner* sp = spanner()) {
     347            0 :         sp->setZ(val);
     348              :     }
     349            0 :     EngravingItem::setZ(val);
     350            0 : }
     351              : 
     352              : //---------------------------------------------------------
     353              : //   nextSegmentElement
     354              : //---------------------------------------------------------
     355              : 
     356            0 : EngravingItem* SpannerSegment::nextSegmentElement()
     357              : {
     358            0 :     return spanner()->nextSegmentElement();
     359              : }
     360              : 
     361              : //---------------------------------------------------------
     362              : //   prevSegmentElement
     363              : //---------------------------------------------------------
     364              : 
     365            0 : EngravingItem* SpannerSegment::prevSegmentElement()
     366              : {
     367            0 :     return spanner()->prevSegmentElement();
     368              : }
     369              : 
     370              : //---------------------------------------------------------
     371              : //   accessibleInfo
     372              : //---------------------------------------------------------
     373              : 
     374            0 : String SpannerSegment::accessibleInfo() const
     375              : {
     376            0 :     return spanner()->accessibleInfo();
     377              : }
     378              : 
     379              : //---------------------------------------------------------
     380              : //   triggerLayout
     381              : //---------------------------------------------------------
     382              : 
     383         1009 : void SpannerSegment::triggerLayout() const
     384              : {
     385         1009 :     if (Spanner* sp = spanner()) {
     386         1009 :         sp->triggerLayout();
     387              :     }
     388         1009 : }
     389              : 
     390           10 : std::list<EngravingObject*> SpannerSegment::linkListForPropertyPropagation() const
     391              : {
     392           10 :     std::list<EngravingObject*> result;
     393           10 :     result.push_back(const_cast<SpannerSegment*>(this));
     394              : 
     395           10 :     if (isMiddleType()) {
     396            0 :         return result;
     397              :     }
     398              : 
     399           10 :     const Spanner* sp = spanner();
     400           20 :     for (const EngravingObject* linkedSpanner : sp->linkList()) {
     401           10 :         if (linkedSpanner == sp || toSpanner(linkedSpanner)->placement() != sp->placement()) {
     402           10 :             continue;
     403              :         }
     404            0 :         const std::vector<SpannerSegment*>& linkedSegments = toSpanner(linkedSpanner)->spannerSegments();
     405            0 :         if (linkedSegments.empty()) {
     406            0 :             continue;
     407              :         }
     408            0 :         if (isSingleBeginType()) {
     409            0 :             result.push_back(linkedSegments.front());
     410            0 :         } else if (isSingleEndType()) {
     411            0 :             result.push_back(linkedSegments.back());
     412              :         }
     413           10 :     }
     414              : 
     415           10 :     return result;
     416            0 : }
     417              : 
     418            0 : bool SpannerSegment::isPropertyLinkedToMaster(Pid id) const
     419              : {
     420            0 :     bool linkedForSpannerSegment = EngravingItem::isPropertyLinkedToMaster(id);
     421            0 :     if (!linkedForSpannerSegment) {
     422            0 :         return false;
     423              :     }
     424              : 
     425              :     // The property is linked for the spanner segment, but may be unlinked for the spanner, in which case we consider it unlinked
     426            0 :     return spanner()->isPropertyLinkedToMaster(id);
     427              : }
     428              : 
     429         1681 : bool SpannerSegment::isUserModified() const
     430              : {
     431         1678 :     bool modified = !autoplace() || !visible()
     432         1678 :                     || (propertyFlags(Pid::MIN_DISTANCE) == PropertyFlags::UNSTYLED
     433         3358 :                         || getProperty(Pid::MIN_DISTANCE) != propertyDefault(Pid::MIN_DISTANCE))
     434         3359 :                     || (!offset().isNull() || !userOff2().isNull());
     435              : 
     436         1681 :     return modified;
     437              : }
     438              : 
     439            0 : bool SpannerSegment::allowTimeAnchor() const
     440              : {
     441            0 :     return spanner()->allowTimeAnchor();
     442              : }
     443              : 
     444            0 : int SpannerSegment::subtype() const
     445              : {
     446            0 :     return spanner()->subtype();
     447              : }
     448              : 
     449            0 : TranslatableString SpannerSegment::subtypeUserName() const
     450              : {
     451            0 :     return spanner()->subtypeUserName();
     452              : }
     453              : 
     454              : //---------------------------------------------------------
     455              : //   Spanner
     456              : //---------------------------------------------------------
     457              : 
     458         4772 : Spanner::Spanner(const ElementType& type, EngravingObject* parent, ElementFlags f)
     459         4772 :     : EngravingItem(type, parent, f)
     460              : {
     461         4772 : }
     462              : 
     463          787 : Spanner::Spanner(const Spanner& s)
     464          787 :     : EngravingItem(s)
     465              : {
     466          787 :     m_playSpanner  = s.m_playSpanner;
     467          787 :     m_startElement = s.m_startElement;
     468          787 :     m_endElement   = s.m_endElement;
     469          787 :     m_tick         = s.m_tick;
     470          787 :     m_ticks        = s.m_ticks;
     471          787 :     m_track2       = s.m_track2;
     472              : 
     473         1376 :     for (auto* segment : s.m_segments) {
     474          589 :         SpannerSegment* newSegment = toSpannerSegment(segment->clone());
     475          589 :         add(newSegment);   // reparents the clone to this spanner
     476              :     }
     477          787 : }
     478              : 
     479         4870 : Spanner::~Spanner()
     480              : {
     481              :     // A spanner owns its segments (they are its DOM children), so it has to free
     482              :     // them. Without this they would only be unparented onto the dummy, where they
     483              :     // would sit until the score is destroyed - with a dangling spanner() pointer.
     484         4870 :     eraseSpannerSegments();
     485         4870 : }
     486              : 
     487              : //---------------------------------------------------------
     488              : //   mag
     489              : //---------------------------------------------------------
     490              : 
     491          140 : double Spanner::mag() const
     492              : {
     493          140 :     if (systemFlag()) {
     494          140 :         return 1.0;
     495              :     }
     496            0 :     return staff() ? staff()->staffMag(tick()) : 1.0;
     497              : }
     498              : 
     499              : //---------------------------------------------------------
     500              : //   add
     501              : //---------------------------------------------------------
     502              : 
     503         4923 : void Spanner::add(EngravingItem* e)
     504              : {
     505         4923 :     SpannerSegment* ls = toSpannerSegment(e);
     506         4923 :     ls->setSpanner(this);
     507         4923 :     ls->setSelected(selected());
     508         4923 :     ls->setTrack(track());
     509              : //      ls->setAutoplace(autoplace());
     510         4923 :     ls->EngravingItem::setZ(z());
     511         4923 :     ls->EngravingItem::setVisible(visible());
     512         4923 :     m_segments.push_back(ls);
     513         4923 :     e->added();
     514         4923 : }
     515              : 
     516              : //---------------------------------------------------------
     517              : //   remove
     518              : //---------------------------------------------------------
     519              : 
     520            0 : void Spanner::remove(EngravingItem* e)
     521              : {
     522            0 :     SpannerSegment* ss = toSpannerSegment(e);
     523            0 :     if (ss->system()) {
     524            0 :         ss->system()->remove(ss);
     525              :     }
     526            0 :     m_segments.erase(std::remove(m_segments.begin(), m_segments.end(), ss), m_segments.end());
     527            0 : }
     528              : 
     529         1108 : void Spanner::removed()
     530              : {
     531         2037 :     for (SpannerSegment* seg : m_segments) {
     532          929 :         seg->moveToSystem(nullptr);
     533              :     }
     534              : 
     535         1137 :     for (SpannerSegment* seg : m_unusedSegments) {
     536           29 :         seg->moveToSystem(nullptr);
     537              :     }
     538         1108 : }
     539              : 
     540           12 : EngravingItemList Spanner::accessibleChildren() const
     541              : {
     542              :     // A segment that is placed on a system belongs to that system in the accessibility
     543              :     // tree, even though this spanner is what owns it.
     544           12 :     EngravingItemList children = EngravingItem::accessibleChildren();
     545           12 :     muse::remove_if(children, [](EngravingItem* item) {
     546           13 :         return item->isSpannerSegment() && toSpannerSegment(item)->system();
     547              :     });
     548              : 
     549           12 :     return children;
     550            0 : }
     551              : 
     552              : //---------------------------------------------------------
     553              : //   removeUnmanaged
     554              : //
     555              : //    Remove the Spanner and its segments from objects which may know about them
     556              : //
     557              : //    This method and the following are used for spanners which are contained within compound elements
     558              : //    which manage their parts themselves without using the standard management supplied by Score;
     559              : //    Example can be the LyricsLine within a Lyrics element or the FiguredBassLine within a FiguredBass
     560              : //    (not implemented yet).
     561              : //---------------------------------------------------------
     562              : 
     563          235 : void Spanner::removeUnmanaged()
     564              : {
     565          477 :     for (SpannerSegment* ss : spannerSegments()) {
     566          242 :         if (ss->system()) {
     567            7 :             ss->moveToSystem(nullptr);
     568              :         }
     569              :     }
     570          235 :     score()->removeUnmanagedSpanner(this);
     571          235 : }
     572              : 
     573              : //---------------------------------------------------------
     574              : //   insertTimeUnmanaged
     575              : //---------------------------------------------------------
     576              : 
     577           19 : void Spanner::insertTimeUnmanaged(const Fraction& fromTick, const Fraction& len)
     578              : {
     579           19 :     Fraction newTick1 = tick();
     580           19 :     Fraction newTick2 = tick2();
     581              : 
     582              :     // check spanner start and end point
     583           19 :     if (len > Fraction(0, 1)) {            // adding time
     584           18 :         if (tick() >= fromTick) {          // start after insertion point: shift start to right
     585            0 :             newTick1 += len;
     586              :         }
     587           18 :         if (tick2() > fromTick) {         // end after insertion point: shift end to right
     588            1 :             newTick2 += len;
     589              :         }
     590              :     }
     591           19 :     if (len < Fraction(0, 1)) {            // removing time
     592            1 :         Fraction toTick = fromTick - len;
     593            1 :         if (tick() > fromTick) {          // start after beginning of removed time
     594            0 :             if (tick() < toTick) {        // start within removed time: bring start at removing point
     595            0 :                 if (ownershipParent()) {
     596            0 :                     ownershipParentItem()->remove(this);
     597            0 :                     return;
     598              :                 } else {
     599            0 :                     newTick1 = fromTick;
     600              :                 }
     601              :             } else {                      // start after removed time: shift start to left
     602            0 :                 newTick1 += len;
     603              :             }
     604              :         }
     605            1 :         if (tick2() > fromTick) {         // end after start of removed time
     606            1 :             if (tick2() < toTick) {       // end within removed time: bring end at removing point
     607            0 :                 newTick2 = fromTick;
     608              :             } else {                      // end after removed time: shift end to left
     609            1 :                 newTick2 += len;
     610              :             }
     611              :         }
     612              :     }
     613              : 
     614              :     // update properties as required
     615           19 :     if (newTick2 <= newTick1) {                 // if no longer any span: remove it
     616            0 :         if (ownershipParent()) {
     617            0 :             ownershipParentItem()->remove(this);
     618              :         }
     619              :     } else {                                    // if either TICKS or TICK did change, update property
     620           19 :         if (newTick2 - newTick1 != tick2() - tick()) {
     621            2 :             setProperty(Pid::SPANNER_TICKS, newTick2 - newTick1);
     622              :         }
     623           19 :         if (newTick1 != tick()) {
     624            0 :             setProperty(Pid::SPANNER_TICK, newTick1);
     625              :         }
     626              :     }
     627              : }
     628              : 
     629              : //---------------------------------------------------------
     630              : //   isVisibleCR
     631              : //---------------------------------------------------------
     632              : 
     633            0 : static bool isVisibleCR(EngravingItem* e)
     634              : {
     635            0 :     if (!e || !e->isChordRest()) {
     636            0 :         return true;  // assume visible
     637              :     }
     638            0 :     ChordRest* cr = toChordRest(e);
     639            0 :     return cr->measure()->visible(cr->staffIdx());
     640              : }
     641              : 
     642              : //---------------------------------------------------------
     643              : //   eitherEndVisible
     644              : //---------------------------------------------------------
     645              : 
     646            0 : bool Spanner::eitherEndVisible() const
     647              : {
     648            0 :     if (isVolta()) {
     649            0 :         return true;
     650              :     }
     651            0 :     if (!score()->staff(staffIdx())->show()) {
     652            0 :         return false;
     653              :     }
     654            0 :     return isVisibleCR(startElement()) || isVisibleCR(endElement());
     655              : }
     656              : 
     657              : //---------------------------------------------------------
     658              : //   setScore
     659              : //---------------------------------------------------------
     660              : 
     661          231 : void Spanner::setScore(Score* s)
     662              : {
     663          231 :     EngravingItem::setScore(s);
     664          369 :     for (SpannerSegment* seg : m_segments) {
     665          138 :         seg->setScore(s);
     666              :     }
     667          231 : }
     668              : 
     669              : //---------------------------------------------------------
     670              : //   getProperty
     671              : //---------------------------------------------------------
     672              : 
     673        21168 : PropertyValue Spanner::getProperty(Pid propertyId) const
     674              : {
     675        21168 :     switch (propertyId) {
     676         2049 :     case Pid::PLAY:
     677         2049 :         return m_playSpanner;
     678          168 :     case Pid::SPANNER_TICK:
     679          168 :         return m_tick;
     680           99 :     case Pid::SPANNER_TICKS:
     681           99 :         return m_ticks;
     682            3 :     case Pid::SPANNER_TRACK2:
     683            3 :         return track2();
     684            0 :     case Pid::LOCATION_STAVES:
     685            0 :         return (track2() / VOICES) - (track() / VOICES);
     686            0 :     case Pid::LOCATION_VOICES:
     687            0 :         return (track2() % VOICES) - (track() / VOICES);
     688            0 :     case Pid::LOCATION_FRACTIONS:
     689            0 :         return m_ticks;
     690            0 :     case Pid::LOCATION_MEASURES:
     691              :     case Pid::LOCATION_GRACE:
     692              :     case Pid::LOCATION_NOTE:
     693            0 :         return Location::getLocationProperty(propertyId, startElement(), endElement());
     694        18849 :     default:
     695        18849 :         break;
     696              :     }
     697        18849 :     return EngravingItem::getProperty(propertyId);
     698              : }
     699              : 
     700              : //---------------------------------------------------------
     701              : //   setProperty
     702              : //---------------------------------------------------------
     703              : 
     704         2005 : bool Spanner::setProperty(Pid propertyId, const PropertyValue& v)
     705              : {
     706         2005 :     switch (propertyId) {
     707            0 :     case Pid::PLAY:
     708            0 :         setPlaySpanner(v.toBool());
     709            0 :         break;
     710           70 :     case Pid::SPANNER_TICK:
     711           70 :         triggerLayout();           // spanner may have moved to another system
     712           70 :         setTick(v.value<Fraction>());
     713           70 :         if (score() && isInSpannerMap() && score()->spannerMap().removeSpanner(this)) {
     714           70 :             score()->addSpanner(this, /*computeStartEnd =*/ false);
     715              :         }
     716           70 :         break;
     717           44 :     case Pid::SPANNER_TICKS:
     718           44 :         triggerLayout();           // spanner may now span for a smaller number of systems
     719           44 :         setTicks(v.value<Fraction>());
     720           44 :         setEndElement(0);                 // invalidate
     721           44 :         break;
     722            1 :     case Pid::TRACK:
     723            1 :         setTrack(v.value<track_idx_t>());
     724            1 :         setStartElement(0);               // invalidate
     725            1 :         break;
     726            1 :     case Pid::SPANNER_TRACK2:
     727            1 :         setTrack2(v.value<track_idx_t>());
     728            1 :         setEndElement(0);                 // invalidate
     729            1 :         break;
     730            0 :     case Pid::POSITION_LINKED_TO_MASTER:
     731            0 :         setPositionLinkedToMaster(v.toBool());
     732            0 :         if (isPositionLinkedToMaster()) {
     733            0 :             for (SpannerSegment* seg : spannerSegments()) {
     734            0 :                 seg->relinkPropertiesToMaster(PropertyGroup::POSITION);
     735              :             }
     736            0 :             relinkPropertiesToMaster(PropertyGroup::POSITION);
     737              :         }
     738            0 :         break;
     739            0 :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     740            0 :         setAppearanceLinkedToMaster(v.toBool());
     741            0 :         if (isAppearanceLinkedToMaster()) {
     742            0 :             for (SpannerSegment* seg : spannerSegments()) {
     743            0 :                 seg->relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
     744              :             }
     745            0 :             relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
     746              :         }
     747            0 :         break;
     748         1889 :     default:
     749         1889 :         return EngravingItem::setProperty(propertyId, v);
     750              :     }
     751          116 :     triggerLayout();
     752          116 :     return true;
     753              : }
     754              : 
     755              : //---------------------------------------------------------
     756              : //   propertyDefault
     757              : //---------------------------------------------------------
     758              : 
     759        21869 : PropertyValue Spanner::propertyDefault(Pid propertyId) const
     760              : {
     761        21869 :     switch (propertyId) {
     762         2049 :     case Pid::PLAY:
     763         2049 :         return true;
     764        19820 :     default:
     765        19820 :         break;
     766              :     }
     767        19820 :     return EngravingItem::propertyDefault(propertyId);
     768              : }
     769              : 
     770              : //---------------------------------------------------------
     771              : //   computeStartElement
     772              : //---------------------------------------------------------
     773              : 
     774         5516 : void Spanner::computeStartElement()
     775              : {
     776         5516 :     EngravingItem* oldStartElement = m_startElement;
     777              : 
     778         5516 :     doComputeStartElement();
     779              : 
     780         5516 :     if (oldStartElement && oldStartElement->isChord()) {
     781         3167 :         toChord(oldStartElement)->removeStartingSpanner(this);
     782              :     }
     783              : 
     784         5516 :     Chord* startChord = m_startElement && m_startElement->isChord() ? toChord(m_startElement) : nullptr;
     785         5516 :     if (startChord) {
     786         3895 :         startChord->addStartingSpanner(this);
     787              :     }
     788         5516 : }
     789              : 
     790         5516 : void Spanner::doComputeStartElement()
     791              : {
     792         5516 :     switch (anchor()) {
     793         3711 :     case Anchor::SEGMENT: {
     794         3711 :         Segment* startSeg = startSegment();
     795         3711 :         if (!startSeg) {
     796          139 :             return;
     797              :         }
     798         3572 :         if (systemFlag()) {
     799           63 :             m_startElement = startSegment();
     800              :         } else {
     801         3509 :             EngravingItem* startEl = startSeg->element(track());
     802         3509 :             if (startEl) {
     803         3508 :                 m_startElement = startEl;
     804              :             } else {
     805            1 :                 m_startElement = startSeg->firstElement(track2staff(track()));
     806              :             }
     807              :         }
     808              :     }
     809         3572 :     break;
     810              : 
     811          789 :     case Anchor::MEASURE:
     812          789 :         m_startElement = score()->tick2measure(tick());
     813          789 :         break;
     814              : 
     815          556 :     case Anchor::CHORDREST:
     816          556 :         m_startElement = startCR();
     817          556 :         break;
     818          460 :     case Anchor::NOTE:
     819          460 :         break;
     820              :     }
     821              : }
     822              : 
     823              : //---------------------------------------------------------
     824              : //   computeEndElement
     825              : //---------------------------------------------------------
     826              : 
     827         5514 : void Spanner::computeEndElement()
     828              : {
     829         5514 :     if (score()->isPaletteScore()) {
     830            0 :         m_endElement = nullptr;
     831            0 :         return;
     832              :     }
     833              : 
     834         5514 :     EngravingItem* oldEndElement = m_endElement;
     835              : 
     836         5514 :     doComputeEndElement();
     837              : 
     838         5514 :     if (oldEndElement && oldEndElement->isChord()) {
     839         2591 :         toChord(oldEndElement)->removeEndingSpanner(this);
     840              :     }
     841              : 
     842         5514 :     Chord* endChord = m_endElement && m_endElement->isChord() ? toChord(m_endElement) : nullptr;
     843         5514 :     if (endChord) {
     844         3400 :         endChord->addEndingSpanner(this);
     845              :     }
     846              : }
     847              : 
     848         4717 : void Spanner::doComputeEndElement()
     849              : {
     850         4717 :     switch (anchor()) {
     851         2912 :     case Anchor::SEGMENT: {
     852         2912 :         Segment* endSeg = endSegment();
     853         2912 :         if (!endSeg) {
     854          124 :             return;
     855              :         }
     856         2788 :         if (systemFlag()) {
     857           62 :             m_endElement = endSeg;
     858              :         } else {
     859         2726 :             track_idx_t trackIdx = effectiveTrack2();
     860         2726 :             EngravingItem* endEl = endSeg->element(trackIdx);
     861         2726 :             if (endEl) {
     862         2716 :                 m_endElement = endEl;
     863              :             } else {
     864           10 :                 m_endElement = endSeg->firstElement(track2staff(trackIdx));
     865              :             }
     866              :         }
     867              :     }
     868         2788 :     break;
     869              : 
     870          789 :     case Anchor::MEASURE:
     871          789 :         m_endElement = score()->tick2measure(tick2() - Fraction(1, 1920));
     872          789 :         break;
     873              : 
     874          460 :     case Anchor::NOTE:
     875          460 :         break;
     876          556 :     case Anchor::CHORDREST:
     877          556 :         m_endElement = endCR();
     878          556 :         break;
     879              :     }
     880              : }
     881              : 
     882        37072 : bool Spanner::canBeCrossStaff() const
     883              : {
     884        37072 :     switch (type()) {
     885        15494 :     case ElementType::SLUR:
     886              :     case ElementType::TIE:
     887              :     case ElementType::ARPEGGIO:
     888              :     case ElementType::CHORD_BRACKET:
     889              :     case ElementType::GLISSANDO:
     890              :     case ElementType::NOTELINE:
     891        15494 :         return true;
     892        21578 :     default:
     893        21578 :         return false;
     894              :     }
     895              : }
     896              : 
     897              : //---------------------------------------------------------
     898              : //   startElementFromSpanner
     899              : //
     900              : //    Given a Spanner and an end element, determines a start element suitable for the end
     901              : //    element of a new Spanner, so that it is 'parallel' to the old one.
     902              : //    Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
     903              : //    (Spanner(const Spanner&) copies start and end elements from the original to the copy).
     904              : //    NOTES:      Only spanners with Anchor::NOTE are currently supported.
     905              : //                Going back from end to start ensures the 'other' anchor of this is already set up
     906              : //                      (for instance, while cloning staves)
     907              : //---------------------------------------------------------
     908              : 
     909           10 : Note* Spanner::startElementFromSpanner(Spanner* sp, EngravingItem* newEnd)
     910              : {
     911           10 :     if (sp->anchor() != Anchor::NOTE) {
     912            0 :         return nullptr;
     913              :     }
     914              : 
     915           10 :     Note* oldStart   = toNote(sp->startElement());
     916           10 :     Note* oldEnd     = toNote(sp->endElement());
     917           10 :     if (oldStart == nullptr || oldEnd == nullptr) {
     918            0 :         return nullptr;
     919              :     }
     920           10 :     Note* newStart   = nullptr;
     921           10 :     Score* score      = newEnd->score();
     922              :     // determine the track where to expect the 'parallel' start element
     923           10 :     track_idx_t newTrack    = (newEnd->track() - oldEnd->track()) + oldStart->track();
     924              :     // look in notes linked to oldStart for a note with the
     925              :     // same score as new score and appropriate track
     926           20 :     for (EngravingObject* newEl : oldStart->linkList()) {
     927           20 :         if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
     928           10 :             newStart = toNote(newEl);
     929           10 :             break;
     930              :         }
     931           10 :     }
     932           10 :     return newStart;
     933              : }
     934              : 
     935              : //---------------------------------------------------------
     936              : //   endElementFromSpanner
     937              : //
     938              : //    Given a Spanner and a start element, determines an end element suitable for the start
     939              : //    element of a new Spanner, so that it is 'parallel' to the old one.
     940              : //    Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
     941              : //    (Spanner(const Spanner&) copies start and end elements from the original to the copy).
     942              : //    NOTES:      Only spanners with Anchor::NOTE are currently supported.
     943              : //---------------------------------------------------------
     944              : 
     945            2 : Note* Spanner::endElementFromSpanner(Spanner* sp, EngravingItem* newStart)
     946              : {
     947            2 :     if (sp->anchor() != Anchor::NOTE) {
     948            0 :         return nullptr;
     949              :     }
     950              : 
     951            2 :     Note* oldStart   = toNote(sp->startElement());
     952            2 :     Note* oldEnd     = toNote(sp->endElement());
     953            2 :     if (oldStart == nullptr || oldEnd == nullptr) {
     954            0 :         return nullptr;
     955              :     }
     956            2 :     Note* newEnd     = nullptr;
     957            2 :     Score* score      = newStart->score();
     958              :     // determine the track where to expect the 'parallel' start element
     959            2 :     track_idx_t newTrack    = newStart->track() + (oldEnd->track() - oldStart->track());
     960              :     // look in notes linked to oldEnd for a note with the
     961              :     // same score as new score and appropriate track
     962            4 :     for (EngravingObject* newEl : oldEnd->linkList()) {
     963            4 :         if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
     964            2 :             newEnd = toNote(newEl);
     965            2 :             break;
     966              :         }
     967            2 :     }
     968            2 :     return newEnd;
     969              : }
     970              : 
     971              : //---------------------------------------------------------
     972              : //   setNoteSpan
     973              : //
     974              : //    Sets up all the variables congruent with given start and end note anchors.
     975              : //---------------------------------------------------------
     976              : 
     977           54 : void Spanner::setNoteSpan(Note* startNote, Note* endNote)
     978              : {
     979           54 :     if (anchor() != Anchor::NOTE) {
     980            0 :         return;
     981              :     }
     982              : 
     983           54 :     Score* score = startNote ? startNote->score() : endNote->score();
     984           54 :     Note* parent = startNote ? startNote : endNote;
     985           54 :     Fraction tick = startNote ? startNote->tick() : endNote->tick();
     986           54 :     Fraction endTick = endNote ? endNote->tick() : startNote->tick();
     987           54 :     track_idx_t track = startNote ? startNote->track() : endNote->track();
     988           54 :     track_idx_t endTrack = endNote ? endNote->track() : startNote->track();
     989              : 
     990           54 :     setScore(score);
     991           54 :     setOwnershipParent(parent);
     992           54 :     setStartElement(startNote);
     993           54 :     setEndElement(endNote);
     994           54 :     setTick(tick);
     995           54 :     setTick2(endTick);
     996           54 :     setTrack(track);
     997           54 :     setTrack2(endTrack);
     998              : }
     999              : 
    1000              : //---------------------------------------------------------
    1001              : //   startChord
    1002              : //---------------------------------------------------------
    1003              : 
    1004         2846 : Chord* Spanner::startChord()
    1005              : {
    1006         2846 :     if (anchor() != Anchor::CHORDREST) {
    1007            8 :         return nullptr;
    1008              :     }
    1009         2838 :     if (!m_startElement) {
    1010            0 :         m_startElement = findStartChord();
    1011              :     }
    1012              : 
    1013         2838 :     if (m_startElement && m_startElement->isChord()) {
    1014         2838 :         return toChord(m_startElement);
    1015              :     }
    1016              : 
    1017            0 :     return nullptr;
    1018              : }
    1019              : 
    1020              : //---------------------------------------------------------
    1021              : //   endChord
    1022              : //---------------------------------------------------------
    1023              : 
    1024         4503 : Chord* Spanner::endChord()
    1025              : {
    1026         4503 :     if (anchor() != Anchor::CHORDREST) {
    1027            8 :         return nullptr;
    1028              :     }
    1029         4495 :     if (!m_endElement && type() == ElementType::SLUR) {
    1030            0 :         m_endElement = findEndChord();
    1031              :     }
    1032              : 
    1033         4495 :     if (m_endElement && m_endElement->isChord()) {
    1034         4480 :         return toChord(m_endElement);
    1035              :     }
    1036              : 
    1037           15 :     return nullptr;
    1038              : }
    1039              : 
    1040              : //---------------------------------------------------------
    1041              : //   startCR
    1042              : //---------------------------------------------------------
    1043              : 
    1044        43586 : ChordRest* Spanner::startCR()
    1045              : {
    1046        43586 :     assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
    1047        43586 :     if (!m_startElement || m_startElement->score() != score()) {
    1048              :         // TODO: This is a bit weird and prevents this method from being const...
    1049           34 :         m_startElement = findStartCR();
    1050              :     }
    1051        43586 :     return (m_startElement && m_startElement->isChordRest()) ? toChordRest(m_startElement) : nullptr;
    1052              : }
    1053              : 
    1054              : //---------------------------------------------------------
    1055              : //   endCR
    1056              : //---------------------------------------------------------
    1057              : 
    1058        48340 : ChordRest* Spanner::endCR()
    1059              : {
    1060        48340 :     assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
    1061        48340 :     if ((!m_endElement || m_endElement->score() != score())) {
    1062              :         // TODO: This is a bit weird and prevents this method from being const...
    1063          360 :         m_endElement = findEndCR();
    1064              :     }
    1065        48340 :     return (m_endElement && m_endElement->isChordRest()) ? toChordRest(m_endElement) : nullptr;
    1066              : }
    1067              : 
    1068              : //---------------------------------------------------------
    1069              : //   findStartChord
    1070              : //---------------------------------------------------------
    1071              : 
    1072            0 : Chord* Spanner::findStartChord() const
    1073              : {
    1074            0 :     assert(anchor() == Anchor::CHORDREST);
    1075            0 :     ChordRest* cr = score()->findCR(tick(), track());
    1076            0 :     return cr && cr->isChord() ? toChord(cr) : nullptr;
    1077              : }
    1078              : 
    1079              : //---------------------------------------------------------
    1080              : //   findEndChord
    1081              : //---------------------------------------------------------
    1082              : 
    1083            0 : Chord* Spanner::findEndChord() const
    1084              : {
    1085            0 :     assert(anchor() == Anchor::CHORDREST);
    1086            0 :     Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
    1087            0 :     ChordRest* endCR = s ? toChordRest(s->element(track2())) : nullptr;
    1088            0 :     if (endCR && !endCR->isChord()) {
    1089            0 :         endCR = nullptr;
    1090              :     }
    1091            0 :     return toChord(endCR);
    1092              : }
    1093              : 
    1094              : //---------------------------------------------------------
    1095              : //   findStartCR
    1096              : //---------------------------------------------------------
    1097              : 
    1098           35 : ChordRest* Spanner::findStartCR() const
    1099              : {
    1100           35 :     assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
    1101           35 :     return score()->findCR(tick(), track());
    1102              : }
    1103              : 
    1104              : //---------------------------------------------------------
    1105              : //   findEndCR
    1106              : //---------------------------------------------------------
    1107              : 
    1108          361 : ChordRest* Spanner::findEndCR() const
    1109              : {
    1110          361 :     assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
    1111          361 :     Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
    1112          361 :     const track_idx_t tr2 = effectiveTrack2();
    1113          361 :     ChordRest* endCR = s ? toChordRest(s->element(tr2)) : nullptr;
    1114          361 :     return endCR;
    1115              : }
    1116              : 
    1117              : //---------------------------------------------------------
    1118              : //   startSegment
    1119              : //---------------------------------------------------------
    1120              : 
    1121         7349 : Segment* Spanner::startSegment() const
    1122              : {
    1123         7349 :     assert(score() != NULL);
    1124              : 
    1125         7349 :     bool mmRest = style().styleB(Sid::createMultiMeasureRests);
    1126         7349 :     Fraction startTick = tick();
    1127         7349 :     track_idx_t trackIdx = track();
    1128         7349 :     staff_idx_t staffIdx = track2staff(trackIdx);
    1129              : 
    1130         7349 :     Segment* startSeg = score()->tick2segment(startTick, true, SegmentType::ChordRest, mmRest);
    1131              : 
    1132         7349 :     if (!startSeg || !startSeg->hasElements(staffIdx) || (isVoiceSpecific() && !startSeg->element(trackIdx))) {
    1133          262 :         startSeg = score()->tick2segment(startTick, true, SegmentType::TimeTick, mmRest);
    1134              :     }
    1135              : 
    1136         7349 :     if (!startSeg && startTick < score()->endTick()) {
    1137            8 :         Measure* measure = mmRest ? score()->tick2measureMM(startTick) : score()->tick2measure(startTick);
    1138            8 :         if (measure) {
    1139            8 :             TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(measure, startTick - measure->tick(), track2staff(trackIdx));
    1140            8 :             IF_ASSERT_FAILED(anchor) {
    1141            0 :                 return nullptr;
    1142              :             }
    1143            8 :             EditTimeTickAnchors::updateLayout(measure);
    1144            8 :             return anchor->segment();
    1145              :         }
    1146              :     }
    1147              : 
    1148         7341 :     if (!startSeg) {
    1149          139 :         startSeg = score()->tick2rightSegment(startTick, mmRest);
    1150              :     }
    1151              : 
    1152         7341 :     return startSeg;
    1153              : }
    1154              : 
    1155              : //---------------------------------------------------------
    1156              : //   endSegment
    1157              : //---------------------------------------------------------
    1158              : 
    1159        10358 : Segment* Spanner::endSegment() const
    1160              : {
    1161        10358 :     assert(score() != NULL);
    1162              : 
    1163        10358 :     bool mmRest = style().styleB(Sid::createMultiMeasureRests);
    1164        10358 :     Fraction endTick = tick2();
    1165        10358 :     track_idx_t trackIdx = effectiveTrack2();
    1166        10358 :     staff_idx_t staffIdx = track2staff(trackIdx);
    1167              : 
    1168        10358 :     Segment* endSeg = score()->tick2segment(endTick, true, SegmentType::ChordRest, mmRest);
    1169              : 
    1170        10358 :     if (!endSeg || !endSeg->hasElements(staffIdx) || (isVoiceSpecific() && !endSeg->element(trackIdx))) {
    1171          505 :         endSeg = score()->tick2segment(endTick, true, SegmentType::TimeTick, mmRest);
    1172              :     }
    1173              : 
    1174        10358 :     if (!endSeg && !endTick.isZero()) {
    1175          277 :         Measure* measure = mmRest ? score()->tick2measureMM(endTick) : score()->tick2measure(endTick);
    1176          277 :         if (measure) {
    1177          156 :             TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(measure, endTick - measure->tick(), track2staff(trackIdx));
    1178          156 :             EditTimeTickAnchors::updateLayout(measure);
    1179          156 :             return anchor->segment();
    1180              :         }
    1181              :     }
    1182              : 
    1183        10202 :     if (!endSeg) {
    1184          124 :         endSeg = score()->tick2leftSegment(endTick, mmRest);
    1185              :     }
    1186              : 
    1187        10202 :     return endSeg;
    1188              : }
    1189              : 
    1190           13 : bool Spanner::elementAppliesToTrack(const track_idx_t refTrack) const
    1191              : {
    1192           13 :     if (!hasVoiceAssignmentProperties()) {
    1193            4 :         return refTrack == track() || refTrack == track2();
    1194              :     }
    1195              : 
    1196            9 :     const VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
    1197              : 
    1198           17 :     return EngravingItem::elementAppliesToTrack(track(), refTrack, voiceAssignment, part()) || EngravingItem::elementAppliesToTrack(
    1199           17 :         track2(), refTrack, voiceAssignment, part());
    1200              : }
    1201              : 
    1202              : //---------------------------------------------------------
    1203              : //   startMeasure
    1204              : //---------------------------------------------------------
    1205              : 
    1206         1035 : Measure* Spanner::startMeasure() const
    1207              : {
    1208         1035 :     return toMeasure(m_startElement);
    1209              : }
    1210              : 
    1211              : //---------------------------------------------------------
    1212              : //   endMeasure
    1213              : //---------------------------------------------------------
    1214              : 
    1215          838 : Measure* Spanner::endMeasure() const
    1216              : {
    1217          838 :     assert(anchor() == Spanner::Anchor::MEASURE);
    1218          838 :     return toMeasure(m_endElement);
    1219              : }
    1220              : 
    1221           15 : Measure* Spanner::findStartMeasure() const
    1222              : {
    1223           15 :     if (!m_startElement) {
    1224            0 :         return nullptr;
    1225              :     }
    1226              : 
    1227           15 :     return toMeasure(m_startElement->findAncestor(ElementType::MEASURE));
    1228              : }
    1229              : 
    1230            3 : Measure* Spanner::findEndMeasure() const
    1231              : {
    1232            3 :     if (!m_endElement) {
    1233            0 :         return nullptr;
    1234              :     }
    1235              : 
    1236            3 :     return toMeasure(m_endElement->findAncestor(ElementType::MEASURE));
    1237              : }
    1238              : 
    1239              : //---------------------------------------------------------
    1240              : //   setSelected
    1241              : //---------------------------------------------------------
    1242              : 
    1243         3879 : void Spanner::setSelected(bool f)
    1244              : {
    1245         3879 :     EngravingItem::setSelected(f);
    1246              : 
    1247         7740 :     for (SpannerSegment* ss : spannerSegments()) {
    1248         3861 :         if (ss->selected() != f) {
    1249           68 :             ss->setSelected(f);
    1250              :         }
    1251              :     }
    1252         3879 : }
    1253              : 
    1254              : //---------------------------------------------------------
    1255              : //   setVisible
    1256              : //---------------------------------------------------------
    1257              : 
    1258         3205 : void Spanner::setVisible(bool f)
    1259              : {
    1260         5956 :     for (SpannerSegment* ss : spannerSegments()) {
    1261         2751 :         ss->EngravingItem::setVisible(f);
    1262              :     }
    1263         3205 :     EngravingItem::setVisible(f);
    1264         3205 : }
    1265              : 
    1266              : //---------------------------------------------------------
    1267              : //   setAutoplace
    1268              : //---------------------------------------------------------
    1269              : 
    1270            2 : void Spanner::setAutoplace(bool f)
    1271              : {
    1272            3 :     for (SpannerSegment* ss : spannerSegments()) {
    1273            1 :         ss->EngravingItem::setAutoplace(f);
    1274              :     }
    1275            2 :     EngravingItem::setAutoplace(f);
    1276            2 : }
    1277              : 
    1278              : //---------------------------------------------------------
    1279              : //   setColor
    1280              : //---------------------------------------------------------
    1281              : 
    1282          908 : void Spanner::setColor(const Color& col)
    1283              : {
    1284          920 :     for (SpannerSegment* ss : spannerSegments()) {
    1285           12 :         ss->EngravingItem::setColor(col);
    1286              :     }
    1287          908 :     EngravingItem::setColor(col);
    1288          908 : }
    1289              : 
    1290              : //---------------------------------------------------------
    1291              : //   setZ
    1292              : //---------------------------------------------------------
    1293              : 
    1294            0 : void Spanner::setZ(int val)
    1295              : {
    1296            0 :     for (SpannerSegment* ss : spannerSegments()) {
    1297            0 :         ss->EngravingItem::setZ(val);
    1298              :     }
    1299            0 :     EngravingItem::setZ(val);
    1300            0 : }
    1301              : 
    1302              : //---------------------------------------------------------
    1303              : //   setStartElement
    1304              : //---------------------------------------------------------
    1305              : 
    1306         4459 : void Spanner::setStartElement(EngravingItem* e)
    1307              : {
    1308              : #ifndef NDEBUG
    1309         4459 :     if (anchor() == Anchor::NOTE) {
    1310         3551 :         assert(!e || e->isNote());
    1311              :     }
    1312              : #endif
    1313         4459 :     m_startElement = e;
    1314         4459 : }
    1315              : 
    1316              : //---------------------------------------------------------
    1317              : //   setEndElement
    1318              : //---------------------------------------------------------
    1319              : 
    1320         5152 : void Spanner::setEndElement(EngravingItem* e)
    1321              : {
    1322              : #ifndef NDEBUG
    1323         5152 :     if (anchor() == Anchor::NOTE) {
    1324         3280 :         assert(!e || e->isNote());
    1325              :     }
    1326              : #endif
    1327         5152 :     m_endElement = e;
    1328         5152 : }
    1329              : 
    1330              : //---------------------------------------------------------
    1331              : //   nextSpanner
    1332              : //---------------------------------------------------------
    1333              : 
    1334            0 : Spanner* Spanner::nextSpanner(EngravingItem* e, staff_idx_t activeStaff)
    1335              : {
    1336            0 :     std::multimap<int, Spanner*> mmap = score()->spanner();
    1337            0 :     auto range = mmap.equal_range(tick().ticks());
    1338            0 :     if (range.first != range.second) {       // range not empty
    1339            0 :         for (auto i = range.first; i != range.second; ++i) {
    1340            0 :             if (i->second == e) {
    1341            0 :                 while (i != range.second) {
    1342            0 :                     ++i;
    1343            0 :                     if (i == range.second) {
    1344            0 :                         return nullptr;
    1345              :                     }
    1346            0 :                     Spanner* s =  i->second;
    1347            0 :                     EngravingItem* st = s->startElement();
    1348            0 :                     if (!st) {
    1349            0 :                         continue;
    1350              :                     }
    1351            0 :                     if (s->startSegment() == toSpanner(e)->startSegment()) {
    1352            0 :                         if (st->staffIdx() == activeStaff) {
    1353            0 :                             return s;
    1354            0 :                         } else if (st->isMeasure() && activeStaff == 0) {
    1355            0 :                             return s;
    1356              :                         }
    1357              :                     }
    1358              :                 }
    1359            0 :                 break;
    1360              :             }
    1361              :         }
    1362              :     }
    1363            0 :     return nullptr;
    1364            0 : }
    1365              : 
    1366              : //---------------------------------------------------------
    1367              : //   prevSpanner
    1368              : //---------------------------------------------------------
    1369              : 
    1370            0 : Spanner* Spanner::prevSpanner(EngravingItem* e, staff_idx_t activeStaff)
    1371              : {
    1372            0 :     std::multimap<int, Spanner*> mmap = score()->spanner();
    1373            0 :     auto range = mmap.equal_range(tick().ticks());
    1374            0 :     if (range.first != range.second) {   // range not empty
    1375            0 :         for (auto i = range.first; i != range.second; ++i) {
    1376            0 :             if (i->second == e) {
    1377            0 :                 if (i == range.first) {
    1378            0 :                     return nullptr;
    1379              :                 }
    1380            0 :                 while (i != range.first) {
    1381            0 :                     --i;
    1382            0 :                     Spanner* s =  i->second;
    1383            0 :                     EngravingItem* st = s->startElement();
    1384            0 :                     if (s->startSegment() == toSpanner(e)->startSegment()) {
    1385            0 :                         if (st->staffIdx() == activeStaff) {
    1386            0 :                             return s;
    1387            0 :                         } else if (st->isMeasure() && activeStaff == 0) {
    1388            0 :                             return s;
    1389              :                         }
    1390              :                     }
    1391              :                 }
    1392            0 :                 break;
    1393              :             }
    1394              :         }
    1395              :     }
    1396            0 :     return nullptr;
    1397            0 : }
    1398              : 
    1399              : //---------------------------------------------------------
    1400              : //   nextSegmentElement
    1401              : //---------------------------------------------------------
    1402              : 
    1403            0 : EngravingItem* Spanner::nextSegmentElement()
    1404              : {
    1405            0 :     Segment* s = startSegment();
    1406            0 :     if (s) {
    1407            0 :         return s->firstElementForNavigation(staffIdx());
    1408              :     }
    1409            0 :     return Navigation::lastElement(score());
    1410              : }
    1411              : 
    1412              : //---------------------------------------------------------
    1413              : //   prevSegmentElement
    1414              : //---------------------------------------------------------
    1415              : 
    1416            0 : EngravingItem* Spanner::prevSegmentElement()
    1417              : {
    1418            0 :     Segment* s = endSegment();
    1419            0 :     if (s) {
    1420            0 :         return s->lastElementForNavigation(staffIdx());
    1421              :     }
    1422            0 :     return Navigation::firstElement(score());
    1423              : }
    1424              : 
    1425              : //---------------------------------------------------------
    1426              : //   setTick
    1427              : //---------------------------------------------------------
    1428              : 
    1429        11984 : void Spanner::setTick(const Fraction& v)
    1430              : {
    1431        11984 :     if (m_tick == v) {
    1432         7843 :         return;
    1433              :     }
    1434              : 
    1435         4141 :     m_tick = v;
    1436              : 
    1437         4141 :     Score* score = this->score();
    1438              : 
    1439         4141 :     if (score && isInSpannerMap()) {
    1440         1444 :         score->spannerMap().setDirty();
    1441              :     }
    1442              : }
    1443              : 
    1444              : //---------------------------------------------------------
    1445              : //   setTick2
    1446              : //---------------------------------------------------------
    1447              : 
    1448         5296 : void Spanner::setTick2(const Fraction& f)
    1449              : {
    1450         5296 :     Fraction ticks = f - m_tick;
    1451         5296 :     IF_ASSERT_FAILED(f >= m_tick) {
    1452            0 :         ticks = m_tick - f;
    1453            0 :         m_tick = f;
    1454              :     }
    1455         5296 :     setTicks(ticks);
    1456         5296 : }
    1457              : 
    1458              : //---------------------------------------------------------
    1459              : //   setTicks
    1460              : //---------------------------------------------------------
    1461              : 
    1462         5766 : void Spanner::setTicks(const Fraction& f)
    1463              : {
    1464         5766 :     if (m_ticks == f) {
    1465         2198 :         return;
    1466              :     }
    1467              : 
    1468         3568 :     IF_ASSERT_FAILED(f.positive()) {
    1469            0 :         m_ticks = -f;
    1470              :     } else {
    1471         3568 :         m_ticks = f;
    1472              :     }
    1473              : 
    1474         3568 :     Score* score = this->score();
    1475              : 
    1476         3568 :     if (score && isInSpannerMap()) {
    1477         1515 :         score->spannerMap().setDirty();
    1478              :     }
    1479              : }
    1480              : 
    1481        16999 : bool Spanner::isVoiceSpecific() const
    1482              : {
    1483              :     static const std::unordered_set<ElementType> VOICE_SPECIFIC_SPANNERS {
    1484              :         ElementType::TRILL,
    1485              :         ElementType::HAIRPIN,
    1486              :         ElementType::LET_RING,
    1487        17021 :     };
    1488              : 
    1489        16999 :     return VOICE_SPECIFIC_SPANNERS.find(type()) != VOICE_SPECIFIC_SPANNERS.end();
    1490              : }
    1491              : 
    1492         5054 : track_idx_t Spanner::track2() const
    1493              : {
    1494         5054 :     return canBeCrossStaff() ? m_track2 : m_track;
    1495              : }
    1496              : 
    1497         5338 : void Spanner::setTrack2(track_idx_t v)
    1498              : {
    1499         5338 :     if (!canBeCrossStaff()) {
    1500         2921 :         return;
    1501              :     }
    1502              : 
    1503         2417 :     m_track2 = v;
    1504              : }
    1505              : 
    1506        26680 : track_idx_t Spanner::effectiveTrack2() const
    1507              : {
    1508        26680 :     return canBeCrossStaff() && m_track2 != muse::nidx ? m_track2 : m_track;
    1509              : }
    1510              : 
    1511              : //---------------------------------------------------------
    1512              : //   triggerLayout
    1513              : //---------------------------------------------------------
    1514              : 
    1515       129541 : void Spanner::triggerLayout() const
    1516              : {
    1517              :     // Don't trigger layout until the tick is correctly set, otherwise is triggers layout of the entire score
    1518       129541 :     if (m_tick.numerator() < 0) {
    1519       116725 :         return;
    1520              :     }
    1521              : 
    1522              :     // Spanners do not have parent even when added to a score, so can't check parent here
    1523        12816 :     const track_idx_t tr2 = effectiveTrack2();
    1524        12816 :     score()->setLayout(m_tick, m_tick + m_ticks, staffIdx(), track2staff(tr2), this);
    1525              : }
    1526              : 
    1527              : //---------------------------------------------------------
    1528              : //   pushUnusedSegment
    1529              : //---------------------------------------------------------
    1530              : 
    1531          471 : void Spanner::pushUnusedSegment(SpannerSegment* seg)
    1532              : {
    1533          471 :     if (!seg) {
    1534            0 :         return;
    1535              :     }
    1536          471 :     seg->moveToSystem(nullptr);
    1537          471 :     m_unusedSegments.push_back(seg);
    1538              : }
    1539              : 
    1540              : //---------------------------------------------------------
    1541              : //   popUnusedSegment
    1542              : //    Take the next unused segment for reusing it.
    1543              : //    If there is no unused segments left returns nullptr.
    1544              : //---------------------------------------------------------
    1545              : 
    1546         3892 : SpannerSegment* Spanner::popUnusedSegment()
    1547              : {
    1548         3892 :     if (m_unusedSegments.empty()) {
    1549         3749 :         return nullptr;
    1550              :     }
    1551          143 :     SpannerSegment* seg = m_unusedSegments.front();
    1552          143 :     m_unusedSegments.pop_front();
    1553          143 :     return seg;
    1554              : }
    1555              : 
    1556              : //---------------------------------------------------------
    1557              : //   reuse
    1558              : //    called when segment from unusedSegments is added
    1559              : //    back to the spanner.
    1560              : //---------------------------------------------------------
    1561              : 
    1562          143 : void Spanner::reuse(SpannerSegment* seg)
    1563              : {
    1564          143 :     add(seg);
    1565          143 : }
    1566              : 
    1567              : //---------------------------------------------------------
    1568              : //   reuseSegments
    1569              : //    Adds \p number segments from unusedSegments to this
    1570              : //    spanner via reuse() call. Returns number of new
    1571              : //    segments that still need to be created, that is,
    1572              : //    returns (number - nMovedSegments).
    1573              : //---------------------------------------------------------
    1574              : 
    1575         2348 : int Spanner::reuseSegments(int number)
    1576              : {
    1577         2491 :     while (number > 0) {
    1578         2348 :         SpannerSegment* seg = popUnusedSegment();
    1579         2348 :         if (!seg) {
    1580         2205 :             break;
    1581              :         }
    1582          143 :         reuse(seg);
    1583          143 :         --number;
    1584              :     }
    1585         2348 :     return number;
    1586              : }
    1587              : 
    1588              : //---------------------------------------------------------
    1589              : //   fixupSegments
    1590              : //    Makes number of segments match targetNumber.
    1591              : //    Tries to reuse unused segments. If there are no
    1592              : //    unused segments left, uses \p createSegment to create
    1593              : //    the needed segments.
    1594              : //    Previously unused segments are added via reuse() call
    1595              : //---------------------------------------------------------
    1596              : 
    1597         5905 : void Spanner::fixupSegments(unsigned int targetNumber, std::function<SpannerSegment* ()> createSegment)
    1598              : {
    1599         5905 :     const int diff = targetNumber - int(nsegments());
    1600         5905 :     if (diff == 0) {
    1601         3086 :         return;
    1602              :     }
    1603         2819 :     if (diff > 0) {
    1604         2348 :         const int ncreate = reuseSegments(diff);
    1605         4881 :         for (int i = 0; i < ncreate; ++i) {
    1606         2533 :             add(createSegment());
    1607              :         }
    1608              :     } else { // diff < 0
    1609          471 :         const int nremove = -diff;
    1610          942 :         for (int i = 0; i < nremove; ++i) {
    1611          471 :             SpannerSegment* seg = m_segments.back();
    1612          471 :             m_segments.pop_back();
    1613          471 :             pushUnusedSegment(seg);
    1614              :         }
    1615              :     }
    1616              : }
    1617              : 
    1618         1130 : bool Spanner::isUserModified() const
    1619              : {
    1620         1868 :     for (SpannerSegment* seg : m_segments) {
    1621          741 :         if (seg->isUserModified()) {
    1622            3 :             return true;
    1623              :         }
    1624              :     }
    1625              : 
    1626         1127 :     return false;
    1627              : }
    1628              : 
    1629              : //---------------------------------------------------------
    1630              : //   eraseSpannerSegments
    1631              : //    Completely erase all spanner segments, both used and
    1632              : //    unused.
    1633              : //---------------------------------------------------------
    1634              : 
    1635         5987 : void Spanner::eraseSpannerSegments()
    1636              : {
    1637              :     // Unplace the used segments explicitly, so that System::remove() calls removed()
    1638              :     // on a fully alive segment. ~SpannerSegment would unplace them too, but from there
    1639              :     // the virtual call no longer reaches overrides below SpannerSegment.
    1640              :     // Unused segments were already unplaced on their way into m_unusedSegments.
    1641         9766 :     for (SpannerSegment* seg : m_segments) {
    1642         3779 :         if (System* system = seg->system()) {
    1643           13 :             system->remove(seg);
    1644              :         }
    1645              :     }
    1646              : 
    1647         5987 :     muse::DeleteAll(m_segments);
    1648         5987 :     muse::DeleteAll(m_unusedSegments);
    1649         5987 :     m_segments.clear();
    1650         5987 :     m_unusedSegments.clear();
    1651         5987 : }
    1652              : 
    1653            0 : String SpannerSegment::formatBarsAndBeats() const
    1654              : {
    1655            0 :     const Spanner* spanner = this->spanner();
    1656              : 
    1657            0 :     if (!spanner) {
    1658            0 :         return EngravingItem::formatBarsAndBeats();
    1659              :     }
    1660              : 
    1661            0 :     const Segment* endSegment = spanner->endSegment();
    1662              : 
    1663            0 :     if (!endSegment) {
    1664            0 :         endSegment = score()->lastSegment()->prev1MM(SegmentType::ChordRest);
    1665              :     }
    1666              : 
    1667            0 :     if (endSegment->tick() > Fraction(0, 1)
    1668            0 :         && endSegment->tick() != score()->lastSegment()->prev1MM(SegmentType::ChordRest)->tick()
    1669            0 :         && spanner->type() != ElementType::SLUR
    1670            0 :         && spanner->type() != ElementType::HAMMER_ON_PULL_OFF
    1671            0 :         && spanner->type() != ElementType::TIE) {
    1672            0 :         endSegment = endSegment->prev1MM(SegmentType::ChordRest);
    1673              :     }
    1674              : 
    1675            0 :     IF_ASSERT_FAILED(endSegment) {
    1676            0 :         return EngravingItem::formatBarsAndBeats();
    1677              :     }
    1678              : 
    1679            0 :     return formatStartBarsAndBeats(spanner->startSegment()) + u' ' + formatEndBarsAndBeats(endSegment);
    1680              : }
    1681              : 
    1682            0 : String SpannerSegment::formatStartBarsAndBeats(const Segment* segment) const
    1683              : {
    1684            0 :     EngravingItem::BarBeat barbeat = segment->barbeat();
    1685            0 :     String result = muse::mtrc("engraving", "Start measure: %1").arg(String::number(barbeat.bar));
    1686              : 
    1687            0 :     if (barbeat.displayedBar != barbeat.bar) {
    1688            0 :         result += u"; " + muse::mtrc("engraving", "Start displayed measure: %1").arg(barbeat.displayedBar);
    1689              :     }
    1690              : 
    1691            0 :     result += u"; " + muse::mtrc("engraving", "Start beat: %1").arg(barbeat.beat);
    1692            0 :     return result;
    1693            0 : }
    1694              : 
    1695            0 : String SpannerSegment::formatEndBarsAndBeats(const Segment* segment) const
    1696              : {
    1697            0 :     EngravingItem::BarBeat barbeat = segment->barbeat();
    1698            0 :     String result = muse::mtrc("engraving", "End measure: %1").arg(String::number(barbeat.bar));
    1699              : 
    1700            0 :     if (barbeat.displayedBar != barbeat.bar) {
    1701            0 :         result += u"; " + muse::mtrc("engraving", "End displayed measure: %1").arg(barbeat.displayedBar);
    1702              :     }
    1703              : 
    1704            0 :     result += u"; " + muse::mtrc("engraving", "End beat: %1").arg(barbeat.beat);
    1705            0 :     return result;
    1706            0 : }
    1707              : 
    1708            0 : bool SpannerSegment::collectForDrawing() const
    1709              : {
    1710            0 :     return EngravingItem::collectForDrawing() && (spanner()->eitherEndVisible() || systemFlag());
    1711              : }
    1712              : }
        

Generated by: LCOV version 2.0-1