LCOV - code coverage report
Current view: top level - src/engraving/dom - engravingitem.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 66.7 % 1570 1047
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              : /**
      24              :  \file
      25              :  Implementation of EngravingItem, ElementList
      26              : */
      27              : 
      28              : #include "engravingitem.h"
      29              : 
      30              : #include <cmath>
      31              : 
      32              : #include "containers.h"
      33              : #include "io/buffer.h"
      34              : #include "translation.h"
      35              : 
      36              : #include "iengravingconfiguration.h"
      37              : #include "iengravingcontextconfiguration.h"
      38              : #include "iengravingfont.h"
      39              : 
      40              : #include "rw/rwregister.h"
      41              : 
      42              : #include "types/typesconv.h"
      43              : 
      44              : #include "rendering/iscorerenderer.h"
      45              : #include "rendering/score/autoplace.h"
      46              : #include "rendering/score/chordlayout.h"
      47              : #include "rendering/score/tlayout.h"
      48              : 
      49              : #ifndef ENGRAVING_NO_ACCESSIBILITY
      50              : #include "accessibility/accessibleitem.h"
      51              : #include "accessibility/accessibleroot.h"
      52              : #endif
      53              : 
      54              : #include "../editing/addremoveelement.h"
      55              : #include "../editing/editdata.h"
      56              : #include "../editing/editproperty.h"
      57              : #include "../editing/elementeditdata.h"
      58              : #include "../editing/navigation.h"
      59              : 
      60              : #include "chord.h"
      61              : #include "dummyparent.h"
      62              : #include "factory.h"
      63              : #include "linkedobjects.h"
      64              : #include "masterscore.h"
      65              : #include "measure.h"
      66              : #include "mscore.h"
      67              : #include "note.h"
      68              : #include "page.h"
      69              : #include "parenthesis.h"
      70              : #include "part.h"
      71              : #include "rootitem.h"
      72              : #include "score.h"
      73              : #include "segment.h"
      74              : #include "shape.h"
      75              : #include "sig.h"
      76              : #include "staff.h"
      77              : #include "stafflines.h"
      78              : #include "stafftype.h"
      79              : #include "system.h"
      80              : 
      81              : #include "log.h"
      82              : #define LOG_PROP() if (0) LOGD()
      83              : 
      84              : using namespace mu;
      85              : using namespace muse::io;
      86              : using namespace muse::draw;
      87              : using namespace mu::engraving;
      88              : using namespace mu::engraving::rendering::score;
      89              : 
      90              : namespace mu::engraving {
      91       621107 : EngravingItem::EngravingItem(const ElementType& type, EngravingObject* parent, ElementFlags f)
      92       621107 :     : EngravingObject(type, parent)
      93              : {
      94       621107 :     m_flags         = f;
      95       621107 :     m_color         = configuration()->defaultColor();
      96       621107 :     m_z             = -1;
      97       621107 :     m_minDistance   = 0.0_sp;
      98       621107 : }
      99              : 
     100        18740 : EngravingItem::EngravingItem(const EngravingItem& e, bool link)
     101        18740 :     : EngravingObject(e)
     102              : {
     103        18740 :     m_offset     = e.m_offset;
     104        18740 :     m_track      = e.m_track;
     105        18740 :     m_flags      = e.m_flags;
     106        18740 :     setFlag(ElementFlag::SELECTED, false);
     107        18740 :     m_z          = e.m_z;
     108        18740 :     m_color      = e.m_color;
     109        18740 :     m_minDistance = e.m_minDistance;
     110        18740 :     m_excludeVerticalAlign = e.m_excludeVerticalAlign;
     111        18740 :     itemDiscovered = false;
     112              : 
     113        18740 :     m_accessibleEnabled = e.m_accessibleEnabled;
     114              : 
     115        18740 :     if (e.m_leftParenthesis) {
     116            0 :         m_leftParenthesis = e.m_leftParenthesis->clone();
     117            0 :         m_leftParenthesis->setOwnershipParent(this);
     118            0 :         m_leftParenthesis->setTrack(track());
     119            0 :         if (link) {
     120            0 :             score()->undo(new Link(m_leftParenthesis, e.m_leftParenthesis));
     121              :         }
     122              :     }
     123        18740 :     if (e.m_rightParenthesis) {
     124            0 :         m_rightParenthesis = e.m_rightParenthesis->clone();
     125            0 :         m_rightParenthesis->setOwnershipParent(this);
     126            0 :         m_rightParenthesis->setTrack(track());
     127            0 :         if (link) {
     128            0 :             score()->undo(new Link(m_rightParenthesis, e.m_rightParenthesis));
     129              :         }
     130              :     }
     131        18740 : }
     132              : 
     133       596767 : EngravingItem::~EngravingItem()
     134              : {
     135       596767 :     Score::onElementDestruction(this);
     136              : 
     137       596767 :     delete m_layoutData;
     138       596767 : }
     139              : 
     140              : #ifndef ENGRAVING_NO_ACCESSIBILITY
     141         4668 : void EngravingItem::setupAccessible()
     142              : {
     143         4668 :     if (m_accessible) {
     144            0 :         return;
     145              :     }
     146              : 
     147              :     static const std::set<ElementType> accessibleDisabled = {
     148              :         ElementType::LEDGER_LINE
     149         4698 :     };
     150              : 
     151         4668 :     if (score() && !score()->isPaletteScore()) {
     152         4668 :         if (!muse::contains(accessibleDisabled, type())) {
     153         4668 :             m_accessible = createAccessible();
     154         4668 :             m_accessible->setup();
     155              :         }
     156              :     }
     157              : }
     158              : 
     159              : #endif
     160              : 
     161         6457 : bool EngravingItem::accessibleEnabled() const
     162              : {
     163         6457 :     return m_accessibleEnabled;
     164              : }
     165              : 
     166       332956 : void EngravingItem::setAccessibleEnabled(bool enabled)
     167              : {
     168       332956 :     m_accessibleEnabled = enabled;
     169       332956 : }
     170              : 
     171          634 : EngravingItem* EngravingItem::accessibleParentItem() const
     172              : {
     173          634 :     if (EngravingItem* p = layoutParent()) {
     174          623 :         return p;
     175              :     }
     176              : 
     177           11 :     if (ownershipParent()) {
     178              :         // attached to something, but not placed anywhere: in neither tree
     179            4 :         return nullptr;
     180              :     }
     181              : 
     182              :     // not attached to anything: head of the tree those objects form
     183            7 :     return score() && score()->dummy() ? score()->dummy()->rootItem() : nullptr;
     184              : }
     185              : 
     186          625 : EngravingItemList EngravingItem::accessibleChildren() const
     187              : {
     188          625 :     return childrenItems();
     189              : }
     190              : 
     191      7904582 : EngravingItem* EngravingItem::ownershipParentItem() const
     192              : {
     193      7904582 :     EngravingObject* p = ownershipParent();
     194      7904582 :     if (p && p->isEngravingItem()) {
     195      7821163 :         return toEngravingItem(p);
     196              :     }
     197              : 
     198        83419 :     return nullptr;
     199              : }
     200              : 
     201      7303967 : EngravingItem* EngravingItem::layoutParent() const
     202              : {
     203      7303967 :     return ownershipParentItem();
     204              : }
     205              : 
     206       734317 : const std::shared_ptr<IEngravingConfiguration>& EngravingItem::configuration() const
     207              : {
     208       734317 :     return score()->configuration.get();
     209              : }
     210              : 
     211         4668 : const muse::modularity::ContextPtr& EngravingItem::iocContext() const
     212              : {
     213         4668 :     return score()->iocContext();
     214              : }
     215              : 
     216         1883 : const std::shared_ptr<IEngravingContextConfiguration>& EngravingItem::contextConfiguration() const
     217              : {
     218         1883 :     return score()->contextConfiguration.get();
     219              : }
     220              : 
     221        66193 : const std::shared_ptr<rendering::IScoreRenderer>& EngravingItem::renderer() const
     222              : {
     223        66193 :     return score()->renderer.get();
     224              : }
     225              : 
     226              : #ifndef ENGRAVING_NO_ACCESSIBILITY
     227            0 : AccessibleItemPtr EngravingItem::createAccessible()
     228              : {
     229            0 :     return std::make_shared<AccessibleItem>(this);
     230              : }
     231              : 
     232        19810 : void EngravingItem::notifyAboutNameChanged()
     233              : {
     234        19810 :     if (!selected()) {
     235        19623 :         return;
     236              :     }
     237              : 
     238          187 :     if (m_accessible) {
     239            0 :         doInitAccessible();
     240            0 :         m_accessible->accessibleRoot()->notifyAboutFocusedElementNameChanged();
     241              :     }
     242              : }
     243              : 
     244              : #endif
     245              : 
     246              : //---------------------------------------------------------
     247              : //   spatiumChanged
     248              : //---------------------------------------------------------
     249              : 
     250         3705 : void EngravingItem::spatiumChanged(double oldValue, double newValue)
     251              : {
     252         3705 :     if (offsetIsSpatiumDependent()) {
     253         3636 :         m_offset *= (newValue / oldValue);
     254              :     }
     255         3705 : }
     256              : 
     257              : //---------------------------------------------------------
     258              : //   localSpatiumChanged
     259              : //    the scale of a staff changed
     260              : //---------------------------------------------------------
     261              : 
     262            0 : void EngravingItem::localSpatiumChanged(double oldValue, double newValue)
     263              : {
     264            0 :     if (offsetIsSpatiumDependent()) {
     265            0 :         m_offset *= (newValue / oldValue);
     266              :     }
     267            0 : }
     268              : 
     269              : //---------------------------------------------------------
     270              : //   spatium
     271              : //---------------------------------------------------------
     272              : 
     273      4798907 : double EngravingItem::spatium() const
     274              : {
     275      4798907 :     EngravingItem* p = layoutParent();
     276      4798907 :     if (systemFlag() || (p && p->systemFlag())) {
     277        43673 :         return style().spatium();
     278              :     }
     279      4755234 :     Staff* s = staff();
     280      4755234 :     return s ? s->spatium(this) : style().spatium();
     281              : }
     282              : 
     283      2224866 : double EngravingItem::defaultSpatium() const
     284              : {
     285      2224866 :     return style().defaultSpatium();
     286              : }
     287              : 
     288          444 : bool EngravingItem::isInteractionAvailable() const
     289              : {
     290          444 :     if (!getProperty(Pid::VISIBLE).toBool() && (score()->printing() || !score()->isShowInvisible())) {
     291            0 :         return false;
     292              :     }
     293              : 
     294          444 :     return true;
     295              : }
     296              : 
     297              : //---------------------------------------------------------
     298              : //   offsetIsSpatiumDependent
     299              : //---------------------------------------------------------
     300              : 
     301        80841 : bool EngravingItem::offsetIsSpatiumDependent() const
     302              : {
     303        80841 :     return sizeIsSpatiumDependent() || (m_flags & ElementFlag::ON_STAFF);
     304              : }
     305              : 
     306            1 : Sid EngravingItem::defaultPosSid() const
     307              : {
     308            1 :     return Sid::NOSTYLE;
     309              : }
     310              : 
     311        75823 : PointF EngravingItem::defaultPos() const
     312              : {
     313        75823 :     Sid styleId = defaultPosSid();
     314              : 
     315        75823 :     if (styleId == Sid::NOSTYLE) {
     316           13 :         return PointF();
     317              :     }
     318              : 
     319        75810 :     PointF offsetPos = style().value(styleId).value<PointF>();
     320        75810 :     if (offsetIsSpatiumDependent()) {
     321        63911 :         offsetPos *= spatium();
     322              :     } else {
     323        11899 :         offsetPos *= DPMM;
     324              :     }
     325              : 
     326        75810 :     return offsetPos;
     327              : }
     328              : 
     329       235200 : PlacementV EngravingItem::placement() const
     330              : {
     331       235200 :     return flag(ElementFlag::PLACE_ABOVE) && !isSystemObjectBelowBottomStaff() ? PlacementV::ABOVE : PlacementV::BELOW;
     332              : }
     333              : 
     334              : //---------------------------------------------------------
     335              : //   magS
     336              : //---------------------------------------------------------
     337              : 
     338      2035330 : double EngravingItem::magS() const
     339              : {
     340      2035330 :     return mag() * (style().spatium() / defaultSpatium());
     341              : }
     342              : 
     343              : //---------------------------------------------------------
     344              : //   name
     345              : //---------------------------------------------------------
     346              : 
     347            0 : TranslatableString EngravingItem::subtypeUserName() const
     348              : {
     349            0 :     return {};
     350              : }
     351              : 
     352           16 : String EngravingItem::translatedSubtypeUserName() const
     353              : {
     354           16 :     return subtypeUserName().translated();
     355              : }
     356              : 
     357              : //---------------------------------------------------------
     358              : //   linkedClone
     359              : //---------------------------------------------------------
     360              : 
     361          733 : EngravingItem* EngravingItem::linkedClone()
     362              : {
     363          733 :     EngravingItem* e = clone();
     364          733 :     score()->undo(new Link(e, this));
     365          733 :     return e;
     366              : }
     367              : 
     368              : //---------------------------------------------------------
     369              : //   deleteLater
     370              : //---------------------------------------------------------
     371              : 
     372          133 : void EngravingItem::deleteLater()
     373              : {
     374          133 :     if (selected()) {
     375            0 :         score()->deselect(this);
     376              :     }
     377          133 :     masterScore()->cmdState().deleteLater(this);
     378          133 : }
     379              : 
     380              : //---------------------------------------------------------
     381              : //   scanElements
     382              : /// If leaf node, apply `func` on this element (after checking if it is visible).
     383              : /// Otherwise, recurse over all children (see ScoreElement::scanElements).
     384              : /// Note: This function is overridden in some classes to skip certain children,
     385              : /// or to apply `func` even to non-leaf nodes.
     386              : //---------------------------------------------------------
     387              : 
     388        11034 : void EngravingItem::scanElements(std::function<void(EngravingItem*)> func)
     389              : {
     390        11034 :     if (m_leftParenthesis) {
     391            2 :         m_leftParenthesis->scanElements(func);
     392              :     }
     393              : 
     394        11034 :     if (m_rightParenthesis) {
     395            2 :         m_rightParenthesis->scanElements(func);
     396              :     }
     397              : 
     398        11034 :     func(this);
     399        11034 : }
     400              : 
     401          780 : bool EngravingItem::collectForDrawing() const
     402              : {
     403          780 :     if (!visible() && !score()->isShowInvisible()) {
     404            0 :         return false;
     405              :     }
     406              : 
     407          780 :     return true;
     408              : }
     409              : 
     410              : //---------------------------------------------------------
     411              : //   reset
     412              : //---------------------------------------------------------
     413              : 
     414           23 : void EngravingItem::reset()
     415              : {
     416           23 :     undoResetProperty(Pid::AUTOPLACE);
     417           23 :     undoResetProperty(Pid::PLACEMENT);
     418           23 :     undoResetProperty(Pid::MIN_DISTANCE);
     419           23 :     undoResetProperty(Pid::OFFSET);
     420           23 :     undoResetProperty(Pid::LEADING_SPACE);
     421           23 :     EngravingObject::reset();
     422           23 : }
     423              : 
     424              : //---------------------------------------------------------
     425              : //   change
     426              : //---------------------------------------------------------
     427              : 
     428          115 : void EngravingItem::change(EngravingItem* o, EngravingItem* n)
     429              : {
     430          115 :     remove(o);
     431          115 :     add(n);
     432          115 : }
     433              : 
     434              : //---------------------------------------------------------
     435              : //   staff
     436              : //---------------------------------------------------------
     437              : 
     438     27447056 : Staff* EngravingItem::staff() const
     439              : {
     440     27447056 :     if (!hasStaff() || score()->staves().empty()) {
     441       970728 :         return nullptr;
     442              :     }
     443              : 
     444     26476328 :     return score()->staff(staffIdx());
     445              : }
     446              : 
     447     27447056 : bool EngravingItem::hasStaff() const
     448              : {
     449     27447056 :     return m_track != muse::nidx;
     450              : }
     451              : 
     452              : //---------------------------------------------------------
     453              : //   staffType
     454              : //---------------------------------------------------------
     455              : 
     456      2607765 : const StaffType* EngravingItem::staffType() const
     457              : {
     458      2607765 :     Staff* s = staff();
     459      2607765 :     return s ? s->staffTypeForElement(this) : nullptr;
     460              : }
     461              : 
     462              : //---------------------------------------------------------
     463              : //   onTabStaff
     464              : //---------------------------------------------------------
     465              : 
     466       815114 : bool EngravingItem::onTabStaff() const
     467              : {
     468       815114 :     const StaffType* stt = staffType();
     469       815114 :     return stt ? stt->isTabStaff() : false;
     470              : }
     471              : 
     472            0 : bool EngravingItem::hasGrips() const
     473              : {
     474            0 :     return gripsCount() > 0;
     475              : }
     476              : 
     477      4964481 : track_idx_t EngravingItem::track() const
     478              : {
     479      4964481 :     return m_track;
     480              : }
     481              : 
     482       540238 : void EngravingItem::setTrack(track_idx_t val)
     483              : {
     484       540238 :     IF_ASSERT_FAILED(val < m_score->ntracks() || val == 0 || val == muse::nidx || m_score->isPaletteScore()) {
     485              :         // Zero and muse::nidx have special use cases.
     486              :         // In all other cases the track can't be larger than the total track count.
     487            0 :         return;
     488              :     }
     489              : 
     490       540238 :     m_track = val;
     491              : 
     492       540238 :     if (m_leftParenthesis) {
     493            2 :         m_leftParenthesis->setTrack(val);
     494              :     }
     495       540238 :     if (m_rightParenthesis) {
     496            2 :         m_rightParenthesis->setTrack(val);
     497              :     }
     498              : }
     499              : 
     500              : //---------------------------------------------------------
     501              : //   z
     502              : //---------------------------------------------------------
     503              : 
     504       107022 : int EngravingItem::z() const
     505              : {
     506       107022 :     if (m_z == -1) {
     507        88939 :         m_z = int(type()) * 100;
     508              :     }
     509       107022 :     return m_z;
     510              : }
     511              : 
     512         6447 : void EngravingItem::setZ(int val)
     513              : {
     514         6447 :     m_z = val;
     515         6447 : }
     516              : 
     517     36139588 : staff_idx_t EngravingItem::staffIdx() const
     518              : {
     519     36139588 :     return track2staff(m_track);
     520              : }
     521              : 
     522         4468 : void EngravingItem::setStaffIdx(staff_idx_t val)
     523              : {
     524         4468 :     voice_idx_t voiceIdx = voice();
     525         4468 :     setTrack(staff2track(val, voiceIdx == muse::nidx ? 0 : voiceIdx));
     526         4468 : }
     527              : 
     528       746540 : staff_idx_t EngravingItem::effectiveStaffIdx() const
     529              : {
     530       746540 :     if (!systemFlag()) {
     531       726486 :         return vStaffIdx();
     532              :     }
     533              : 
     534        20054 :     const System* system = toSystem(findAncestor(ElementType::SYSTEM));
     535        20054 :     if (!system || system->vbox()) {
     536            0 :         return vStaffIdx();
     537              :     }
     538              : 
     539        20054 :     if (isSystemObjectBelowBottomStaff()) {
     540            0 :         return system->lastVisibleStaff();
     541              :     }
     542              : 
     543        20054 :     staff_idx_t originalStaffIdx = staffIdx();
     544        20054 :     if (originalStaffIdx == muse::nidx) {
     545           44 :         return muse::nidx;
     546              :     }
     547              : 
     548        20010 :     const Measure* m = findMeasure();
     549              : 
     550              :     // TODO - make more system markings ignore empty cutaway measures
     551        20628 :     auto cutawayMeasureNo =[&](staff_idx_t staffIdx) {
     552        20628 :         if (!m) {
     553          890 :             return false;
     554              :         }
     555        19738 :         bool measureNo = isMeasureNumber();
     556        19738 :         bool staffVisible = m->mstaves()[staffIdx]->visible();
     557        19738 :         bool cutaway = score()->staff(staffIdx)->cutaway() && m->isEmpty(staffIdx);
     558        19738 :         return (cutaway || !staffVisible) && measureNo;
     559        20010 :     };
     560              : 
     561        20010 :     const std::vector<Staff*>& systemObjectStaves = m_score->systemObjectStaves(); // CAUTION: may not be ordered
     562        20010 :     if (originalStaffIdx > 0) {
     563          630 :         staff_idx_t prevSysObjStaffIdx = 0;
     564         2216 :         for (Staff* sysObjStaff : systemObjectStaves) {
     565         1586 :             staff_idx_t sysObjStaffIdx = sysObjStaff->idx();
     566         1586 :             if (sysObjStaffIdx > prevSysObjStaffIdx && sysObjStaffIdx < originalStaffIdx) {
     567          430 :                 prevSysObjStaffIdx = sysObjStaffIdx;
     568              :             }
     569              :         }
     570              : 
     571          630 :         bool omitObject = true;
     572          630 :         for (staff_idx_t stfIdx = prevSysObjStaffIdx; stfIdx < originalStaffIdx; ++stfIdx) {
     573          630 :             if (m_score->staff(stfIdx)->show() && system->staff(stfIdx)->show() && !cutawayMeasureNo(stfIdx)) {
     574          630 :                 omitObject = false;
     575          630 :                 break;
     576              :             }
     577              :         }
     578              : 
     579          630 :         if (omitObject) {
     580              :             // If all staves between this and the previous system object are hidden
     581              :             // we omit this object because it will be replaced by the upper one
     582            0 :             return muse::nidx;
     583              :         }
     584              :     }
     585              : 
     586        20010 :     if (m_score->staff(originalStaffIdx)->show() && system->staff(originalStaffIdx)->show() && !cutawayMeasureNo(originalStaffIdx)) {
     587        19998 :         return originalStaffIdx;
     588              :     }
     589              : 
     590           12 :     return system->nextVisibleStaff(originalStaffIdx);
     591              : }
     592              : 
     593         2446 : bool EngravingItem::isTopSystemObject() const
     594              : {
     595         2446 :     return systemFlag() && track() == 0;
     596              : }
     597              : 
     598      1531646 : staff_idx_t EngravingItem::vStaffIdx() const
     599              : {
     600      1531646 :     return staffIdx();
     601              : }
     602              : 
     603      2501028 : voice_idx_t EngravingItem::voice() const
     604              : {
     605      2501028 :     return track2voice(m_track);
     606              : }
     607              : 
     608           56 : void EngravingItem::setVoice(voice_idx_t v)
     609              : {
     610           56 :     setTrack((m_track / VOICES) * VOICES + v);
     611           56 : }
     612              : 
     613              : //---------------------------------------------------------
     614              : //   tick
     615              : //---------------------------------------------------------
     616              : 
     617      6946007 : Fraction EngravingItem::tick() const
     618              : {
     619              :     // walks EngravingObject, because the chain leaves EngravingItem at the score
     620      8862146 :     for (const EngravingObject* e = ownershipParent(); e; e = e->ownershipParent()) {
     621      8386078 :         if (e->isSegment()) {
     622      6229567 :             return toSegment(e)->tick();
     623      2156511 :         } else if (e->isMeasureBase()) {
     624       240372 :             return toMeasureBase(e)->tick();
     625              :         }
     626              :     }
     627       476068 :     return Fraction(0, 1);
     628              : }
     629              : 
     630              : //---------------------------------------------------------
     631              : //   rtick
     632              : //---------------------------------------------------------
     633              : 
     634      1482281 : Fraction EngravingItem::rtick() const
     635              : {
     636      2647439 :     for (const EngravingObject* e = ownershipParent(); e; e = e->ownershipParent()) {
     637      2647349 :         if (e->isSegment()) {
     638      1482191 :             return toSegment(e)->rtick();
     639              :         }
     640              :     }
     641           90 :     return Fraction(0, 1);
     642              : }
     643              : 
     644              : //---------------------------------------------------------
     645              : //   beat
     646              : //---------------------------------------------------------
     647              : 
     648            0 : Fraction EngravingItem::beat() const
     649              : {
     650              :     // Returns an appropriate fraction of ticks for use as a "Beat" reference
     651              :     // in the Select All Similar filter.
     652              :     int bar, beat, ticks;
     653            0 :     TimeSigMap* tsm = score()->sigmap();
     654            0 :     tsm->tickValues(tick().ticks(), &bar, &beat, &ticks);
     655            0 :     int ticksB = ticks_beat(tsm->timesig(tick().ticks()).timesig().denominator());
     656              : 
     657            0 :     Fraction complexFraction((++beat * ticksB) + ticks, ticksB);
     658            0 :     return complexFraction.reduced();
     659              : }
     660              : 
     661              : //---------------------------------------------------------
     662              : //   part
     663              : //---------------------------------------------------------
     664              : 
     665       678610 : Part* EngravingItem::part() const
     666              : {
     667       678610 :     Staff* s = staff();
     668       678610 :     return s ? s->part() : 0;
     669              : }
     670              : 
     671       301215 : Part* EngravingItem::originPart() const
     672              : {
     673       301215 :     Part* p = part();
     674       301215 :     return p ? p->originPart(track(), tick()) : nullptr;
     675              : }
     676              : 
     677       298720 : void EngravingItem::setColor(const Color& c)
     678              : {
     679       298720 :     m_color = c;
     680       298720 : }
     681              : 
     682       153228 : Color EngravingItem::color() const
     683              : {
     684       153228 :     return m_color;
     685              : }
     686              : 
     687              : //---------------------------------------------------------
     688              : //   curColor
     689              : //---------------------------------------------------------
     690              : 
     691          385 : Color EngravingItem::curColor(const rendering::PaintOptions& opt) const
     692              : {
     693          385 :     return curColor(getProperty(Pid::VISIBLE).toBool(), opt);
     694              : }
     695              : 
     696              : //---------------------------------------------------------
     697              : //   curColor
     698              : //---------------------------------------------------------
     699              : 
     700          385 : Color EngravingItem::curColor(bool isVisible, const rendering::PaintOptions& opt) const
     701              : {
     702          385 :     return curColor(isVisible, color(), opt);
     703              : }
     704              : 
     705          385 : Color EngravingItem::curColor(bool isVisible, Color normalColor, const rendering::PaintOptions& opt) const
     706              : {
     707          385 :     if (opt.isPrinting) {
     708              :         // For printing, always use the actual color of the item (default black unless overridden by the user)
     709          385 :         return normalColor;
     710              :     }
     711              : 
     712            0 :     if (flag(ElementFlag::DROP_TARGET)) {
     713            0 :         return configuration()->highlightSelectionColor(track() == muse::nidx ? 0 : voice());
     714              :     }
     715              : 
     716            0 :     bool marked = false;
     717            0 :     if (isNote()) {
     718            0 :         marked = toNote(this)->mark();
     719              :     }
     720              : 
     721            0 :     auto maybeOverrideColor = [&](Color c) -> Color {
     722            0 :         if (opt.overrideItemColor) {
     723            0 :             return opt.overrideItemColor(this, c);
     724              :         }
     725            0 :         return c;
     726            0 :     };
     727              : 
     728            0 :     if (selected() || marked) {
     729            0 :         voice_idx_t voiceForColorChoice = track() == muse::nidx ? 0 : voice();
     730            0 :         if (hasVoiceAssignmentProperties()) {
     731            0 :             VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
     732            0 :             if (voiceAssignment != VoiceAssignment::CURRENT_VOICE_ONLY) {
     733            0 :                 voiceForColorChoice = VOICES;
     734              :             }
     735              :         }
     736              :         return maybeOverrideColor(
     737            0 :             configuration()->selectionColor(voiceForColorChoice, isVisible, isUnlinkedFromMaster()));
     738              :     }
     739              : 
     740            0 :     if (!isVisible) {
     741            0 :         return configuration()->invisibleColor();
     742              :     }
     743              : 
     744            0 :     if (!opt.ignoreDisplayedDefaultColor && normalColor == configuration()->defaultColor()) {
     745            0 :         return maybeOverrideColor(configuration()->displayedDefaultColor(opt.invertColors));
     746              :     }
     747              : 
     748            0 :     if (opt.invertColors) {
     749            0 :         return maybeOverrideColor(normalColor.inverted());
     750              :     }
     751            0 :     return maybeOverrideColor(normalColor);
     752              : }
     753              : 
     754        17705 : PointF EngravingItem::systemPos() const
     755              : {
     756              :     // Returns position in system coordinates. Only applicable to items
     757              :     // that have a System ancestor.
     758              : 
     759        17705 :     IF_ASSERT_FAILED(findAncestor(ElementType::SYSTEM)) {
     760            0 :         return PointF();
     761              :     }
     762              : 
     763        17705 :     PointF result = pos();
     764        17705 :     EngravingItem* ancestor = layoutParent();
     765        50525 :     while (ancestor && !ancestor->isSystem()) {
     766        32820 :         result += ancestor->pos();
     767        32820 :         ancestor = ancestor->layoutParent();
     768              :     }
     769              : 
     770        17705 :     return result;
     771              : }
     772              : 
     773              : //---------------------------------------------------------
     774              : //   pagePos
     775              : //    return position in canvas coordinates
     776              : //---------------------------------------------------------
     777              : 
     778       649949 : PointF EngravingItem::pagePos() const
     779              : {
     780       649949 :     PointF p(pos());
     781       649949 :     EngravingItem* parent = layoutParent();
     782       649949 :     if (parent == nullptr) {
     783         4699 :         return p;
     784              :     }
     785              : 
     786       645250 :     staff_idx_t idx = effectiveStaffIdx();
     787              : 
     788       645250 :     if (idx == muse::nidx) {
     789        12615 :         idx = vStaffIdx();
     790              :     }
     791              : 
     792       271608 :     auto measureStaffY = [this](const Measure* measure, staff_idx_t idx) -> double {
     793       271608 :         if (!measure) {
     794            0 :             return 0.0;
     795              :         }
     796       271608 :         if (measure->mstaves().size() <= idx) {
     797            0 :             LOGD("staffIdx out of bounds: %s", typeName());
     798            0 :             return 0.0;
     799              :         }
     800       271608 :         return measure->staffLines(idx)->y();
     801       645250 :     };
     802              : 
     803       507839 :     auto systemStaffY = [this](const System* system, staff_idx_t idx) -> double {
     804       507839 :         if (!system) {
     805          366 :             return 0.0;
     806              :         }
     807       507473 :         if (system->staves().size() <= idx) {
     808            0 :             LOGD("staffIdx out of bounds: %s", typeName());
     809            0 :             return 0.0;
     810              :         }
     811       507473 :         return system->staffYpage(idx);
     812       645250 :     };
     813              : 
     814       645250 :     if (m_flags & ElementFlag::ON_STAFF) {
     815              :         // Segments, measures and systems all contain multiple staves
     816              :         // Find the position of the stave that this item is on
     817       512172 :         if (parent->isSegment()) {
     818       266448 :             Segment* segment = toSegment(parent);
     819       266448 :             Measure* measure = segment ? segment->measure() : nullptr;
     820       266448 :             System* system = measure ? measure->system() : nullptr;
     821       266448 :             p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx);
     822       245724 :         } else if (parent->isMeasure()) {
     823         5160 :             Measure* measure = toMeasure(parent);
     824         5160 :             System* system = measure->system();
     825         5160 :             p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx);
     826       240564 :         } else if (parent->isSystem()) {
     827       236231 :             System* system = toSystem(parent);
     828       236231 :             p.ry() += systemStaffY(system, idx);
     829              :         } else {
     830         4333 :             return p + parent->pagePos();
     831              :         }
     832       507839 :         p.rx() = pageX();
     833              :     } else {
     834       133078 :         if (parent->layoutParent()) {
     835       123558 :             p += parent->pagePos();
     836              :         }
     837              :     }
     838       640917 :     return p;
     839              : }
     840              : 
     841              : //---------------------------------------------------------
     842              : //   canvasPos
     843              : //---------------------------------------------------------
     844              : 
     845        76138 : PointF EngravingItem::canvasPos() const
     846              : {
     847        76138 :     PointF p(pos());
     848        76138 :     EngravingItem* parent = layoutParent();
     849        76138 :     if (parent == nullptr) {
     850         1212 :         return p;
     851              :     }
     852              : 
     853        74926 :     staff_idx_t idx = effectiveStaffIdx();
     854              : 
     855        74926 :     if (idx == muse::nidx) {
     856         1633 :         idx = vStaffIdx();
     857              :     }
     858              : 
     859        38623 :     auto measureStaffY = [this](const Measure* measure, staff_idx_t idx) -> double {
     860        38623 :         if (!measure) {
     861            0 :             return 0.0;
     862              :         }
     863        38623 :         if (measure->mstaves().size() <= idx) {
     864         1174 :             LOGD("staffIdx out of bounds: %s", typeName());
     865         1174 :             return 0.0;
     866              :         }
     867        37449 :         return measure->staffLines(idx)->y();
     868        74926 :     };
     869              : 
     870        42373 :     auto systemStaffY = [this](const System* system, staff_idx_t idx) -> double {
     871        42373 :         if (!system) {
     872         1118 :             return 0.0;
     873              :         }
     874        41255 :         if (system->staves().size() <= idx) {
     875           74 :             LOGD("staffIdx out of bounds: %s", typeName());
     876           74 :             return 0.0;
     877              :         }
     878        41181 :         return system->staffYpage(idx);
     879        74926 :     };
     880              : 
     881        38623 :     auto pageY = [](const Page* page) -> double {
     882        38623 :         if (!page) {
     883        15660 :             return 0.0;
     884              :         }
     885        22963 :         return page->y();
     886              :     };
     887              : 
     888        74926 :     if (m_flags & ElementFlag::ON_STAFF) {
     889              :         // Segments, measures and systems all contain multiple staves
     890              :         // Find the position of the stave that this item is on
     891        46719 :         if (parent->isSegment()) {
     892        38399 :             Segment* segment = toSegment(parent);
     893        38399 :             Measure* measure = segment ? segment->measure() : nullptr;
     894        38399 :             System* system = measure ? measure->system() : nullptr;
     895        38399 :             Page* page = system ? system->page() : nullptr;
     896        38399 :             p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx) + pageY(page);
     897         8320 :         } else if (parent->isMeasure()) {
     898          224 :             Measure* measure = toMeasure(parent)->coveringMMRestOrThis();
     899          224 :             System* system = measure->system();
     900          224 :             Page* page = system->page();
     901          224 :             p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx) + pageY(page);
     902         8096 :         } else if (parent->isSystem()) {
     903         3750 :             System* system = toSystem(parent);
     904         3750 :             p.ry() += systemStaffY(system, idx);
     905              :         } else {
     906         4346 :             return p + parent->canvasPos();
     907              :         }
     908        42373 :         p.rx() = canvasX();
     909              :     } else {
     910        28207 :         p += parent->canvasPos();
     911              :     }
     912        70580 :     return p;
     913              : }
     914              : 
     915              : //---------------------------------------------------------
     916              : //   pageX
     917              : //---------------------------------------------------------
     918              : 
     919       546681 : double EngravingItem::pageX() const
     920              : {
     921       546681 :     double xp = x();
     922      1228924 :     for (EngravingItem* e = layoutParent(); e && e->layoutParent(); e = e->layoutParent()) {
     923       682243 :         xp += e->x();
     924              :     }
     925       546681 :     return xp;
     926              : }
     927              : 
     928              : //---------------------------------------------------------
     929              : //    canvasX
     930              : //---------------------------------------------------------
     931              : 
     932        42580 : double EngravingItem::canvasX() const
     933              : {
     934        42580 :     double xp = x();
     935       187435 :     for (EngravingItem* e = layoutParent(); e; e = e->layoutParent()) {
     936       144855 :         xp += e->x();
     937              :     }
     938        42580 :     return xp;
     939              : }
     940              : 
     941              : //---------------------------------------------------------
     942              : //   contains
     943              : //    Return true if p is inside the shape of the object.
     944              : //    Note: p is in page coordinates
     945              : //---------------------------------------------------------
     946              : 
     947            0 : bool EngravingItem::contains(const PointF& p) const
     948              : {
     949            0 :     return shape().contains(p - pagePos());
     950              : }
     951              : 
     952              : //---------------------------------------------------------
     953              : //  intersects
     954              : //    Return true if \a rr intersects bounding box of object.
     955              : //    Note: \a rr is in page coordinates
     956              : //---------------------------------------------------------
     957              : 
     958            0 : bool EngravingItem::intersects(const RectF& rr) const
     959              : {
     960            0 :     return shape().intersects(rr.translated(-pagePos()));
     961              : }
     962              : 
     963            0 : bool EngravingItem::hitShapeContains(const PointF& p) const
     964              : {
     965            0 :     return hitShape().contains(p - pagePos());
     966              : }
     967              : 
     968            0 : bool EngravingItem::hitShapeIntersects(const RectF& rr) const
     969              : {
     970            0 :     return hitShape().intersects(rr.translated(-pagePos()));
     971              : }
     972              : 
     973              : //---------------------------------------------------------
     974              : //   remove
     975              : ///   Remove \a el from the list. Return true on success.
     976              : //---------------------------------------------------------
     977              : 
     978           85 : bool ElementList::remove(EngravingItem* el)
     979              : {
     980           85 :     auto i = find(begin(), end(), el);
     981           85 :     if (i == end()) {
     982            0 :         return false;
     983              :     }
     984           85 :     erase(i);
     985           85 :     return true;
     986              : }
     987              : 
     988              : //---------------------------------------------------------
     989              : //   replace
     990              : //---------------------------------------------------------
     991              : 
     992            0 : void ElementList::replace(EngravingItem* o, EngravingItem* n)
     993              : {
     994            0 :     auto i = find(begin(), end(), o);
     995            0 :     if (i == end()) {
     996            0 :         LOGD("ElementList::replace: element not found");
     997            0 :         return;
     998              :     }
     999            0 :     *i = n;
    1000              : }
    1001              : 
    1002              : //---------------------------------------------------------
    1003              : //   dump
    1004              : //---------------------------------------------------------
    1005              : 
    1006            0 : void EngravingItem::dump() const
    1007              : {
    1008            0 :     const LayoutData* ldata = this->ldata();
    1009            0 :     LOGD("---EngravingItem: %s, pos(%4.2f,%4.2f)"
    1010              :          "\n   bbox(%g,%g,%g,%g)"
    1011              :          "\n   abox(%g,%g,%g,%g)"
    1012              :          "\n  parent: %p",
    1013            0 :          typeName(), ldata->pos().x(), ldata->pos().y(),
    1014            0 :          ldata->bbox().x(), ldata->bbox().y(), ldata->bbox().width(), ldata->bbox().height(),
    1015            0 :          pageBoundingRect().x(), pageBoundingRect().y(), pageBoundingRect().width(), pageBoundingRect().height(),
    1016              :          ownershipParent());
    1017            0 : }
    1018              : 
    1019              : //---------------------------------------------------------
    1020              : //   mimeData
    1021              : //---------------------------------------------------------
    1022              : 
    1023           16 : muse::ByteArray EngravingItem::mimeData(const PointF& dragOffset) const
    1024              : {
    1025           16 :     auto buffer = Buffer::opened(IODevice::WriteOnly);
    1026           16 :     XmlWriter xml(&buffer);
    1027              : 
    1028           16 :     xml.startElement("EngravingItem");
    1029           16 :     if (isNote()) {
    1030           15 :         xml.tagFraction("duration", toNote(this)->chord()->ticks());
    1031              :     }
    1032           16 :     if (!dragOffset.isNull()) {
    1033            0 :         xml.tagPoint("dragOffset", dragOffset);
    1034              :     }
    1035              : 
    1036           16 :     rw::RWRegister::writer()->writeItem(this, xml);
    1037              : 
    1038           16 :     xml.endElement();
    1039           16 :     xml.flush();
    1040           16 :     buffer.close();
    1041           32 :     return buffer.data();
    1042           16 : }
    1043              : 
    1044              : //---------------------------------------------------------
    1045              : //   readType
    1046              : //    return new position of QDomElement in e
    1047              : //---------------------------------------------------------
    1048              : 
    1049           16 : ElementType EngravingItem::readType(XmlReader& e, PointF* dragOffset, Fraction* duration)
    1050              : {
    1051           16 :     while (e.readNextStartElement()) {
    1052           16 :         if (e.name() == "EngravingItem") {
    1053           31 :             while (e.readNextStartElement()) {
    1054           31 :                 const AsciiStringView tag = e.name();
    1055           31 :                 if (tag == "dragOffset") {
    1056            0 :                     *dragOffset = e.readPoint();
    1057           31 :                 } else if (tag == "duration") {
    1058           15 :                     *duration = e.readFraction();
    1059              :                 } else {
    1060           16 :                     ElementType type = TConv::fromXml(tag, ElementType::INVALID);
    1061           16 :                     if (type == ElementType::INVALID) {
    1062            0 :                         break;
    1063              :                     }
    1064           16 :                     return type;
    1065              :                 }
    1066              :             }
    1067              :         } else {
    1068            0 :             e.unknown();
    1069              :         }
    1070              :     }
    1071            0 :     return ElementType::INVALID;
    1072              : }
    1073              : 
    1074              : //---------------------------------------------------------
    1075              : //   readMimeData
    1076              : //---------------------------------------------------------
    1077              : 
    1078           16 : EngravingItem* EngravingItem::readMimeData(Score* score, const muse::ByteArray& data, PointF* dragOffset, Fraction* duration)
    1079              : {
    1080           16 :     XmlReader e(data);
    1081              : 
    1082           16 :     const ElementType type = EngravingItem::readType(e, dragOffset, duration);
    1083           16 :     if (type == ElementType::INVALID) {
    1084            0 :         LOGD("cannot read type");
    1085            0 :         return nullptr;
    1086              :     }
    1087              : 
    1088           16 :     EngravingItem* el = Factory::createItem(type, score->dummy(), false);
    1089           16 :     if (el) {
    1090           16 :         rw::RWRegister::reader()->readItem(el, e);
    1091              :     }
    1092              : 
    1093           16 :     return el;
    1094           16 : }
    1095              : 
    1096              : //---------------------------------------------------------
    1097              : //   add
    1098              : //---------------------------------------------------------
    1099              : 
    1100           40 : void EngravingItem::add(EngravingItem* e)
    1101              : {
    1102           40 :     switch (e->type()) {
    1103           40 :     case ElementType::PARENTHESIS: {
    1104           40 :         Parenthesis* p = toParenthesis(e);
    1105           40 :         p->setVisible(visible());
    1106           40 :         if (p->direction() == DirectionH::LEFT) {
    1107           20 :             m_leftParenthesis = p;
    1108           20 :         } else if (p->direction() == DirectionH::RIGHT) {
    1109           20 :             m_rightParenthesis = p;
    1110              :         }
    1111           40 :         break;
    1112              :     }
    1113            0 :     default:
    1114            0 :         LOGD("EngravingItem: cannot add %s to %s", e->typeName(), typeName());
    1115              :     }
    1116           40 : }
    1117              : 
    1118              : //---------------------------------------------------------
    1119              : //   remove
    1120              : //---------------------------------------------------------
    1121              : 
    1122            0 : void EngravingItem::remove(EngravingItem* e)
    1123              : {
    1124            0 :     switch (e->type()) {
    1125            0 :     case ElementType::PARENTHESIS: {
    1126            0 :         if (e == m_leftParenthesis) {
    1127            0 :             m_leftParenthesis = nullptr;
    1128              :         }
    1129            0 :         if (e == m_rightParenthesis) {
    1130            0 :             m_rightParenthesis = nullptr;
    1131              :         }
    1132            0 :         break;
    1133              :     }
    1134            0 :     default:
    1135            0 :         ASSERT_X(String(u"EngravingItem: cannot remove %1 from %2").arg(String::fromAscii(e->typeName()), String::fromAscii(typeName())));
    1136              :     }
    1137            0 : }
    1138              : 
    1139              : //---------------------------------------------------------
    1140              : //   elementLessThan
    1141              : //---------------------------------------------------------
    1142              : 
    1143         2366 : bool elementLessThan(const EngravingItem* const e1, const EngravingItem* const e2)
    1144              : {
    1145         2366 :     if (e1->z() == e2->z()) {
    1146         1133 :         if (e1->selected() && !e2->selected()) {
    1147            0 :             return false;
    1148              :         }
    1149         1133 :         if (!e1->selected() && e2->selected()) {
    1150            0 :             return true;
    1151              :         }
    1152         1133 :         if (e1->visible() && !e2->visible()) {
    1153            0 :             return false;
    1154              :         }
    1155         1133 :         if (!e1->visible() && e2->visible()) {
    1156            0 :             return true;
    1157              :         }
    1158              : 
    1159         1133 :         return e1->track() < e2->track();
    1160              :     }
    1161              : 
    1162         1233 :     return e1->z() < e2->z();
    1163              : }
    1164              : 
    1165              : //---------------------------------------------------------
    1166              : //   autoplace
    1167              : //---------------------------------------------------------
    1168              : 
    1169       155165 : bool EngravingItem::autoplace() const
    1170              : {
    1171       155165 :     if (!score() || !style().styleB(Sid::autoplaceEnabled)) {
    1172            0 :         return false;
    1173              :     }
    1174       155165 :     return !flag(ElementFlag::NO_AUTOPLACE);
    1175              : }
    1176              : 
    1177              : //---------------------------------------------------------
    1178              : //   getProperty
    1179              : //---------------------------------------------------------
    1180              : 
    1181       802791 : PropertyValue EngravingItem::getProperty(Pid propertyId) const
    1182              : {
    1183       802791 :     switch (propertyId) {
    1184           47 :     case Pid::TICK:
    1185           47 :         return tick();
    1186            3 :     case Pid::TRACK:
    1187            3 :         return track();
    1188            0 :     case Pid::VOICE:
    1189            0 :         return voice();
    1190         2847 :     case Pid::GENERATED:
    1191         2847 :         return generated();
    1192        90018 :     case Pid::COLOR:
    1193        90018 :         return PropertyValue::fromValue(color());
    1194        79831 :     case Pid::VISIBLE:
    1195        79831 :         return visible();
    1196            0 :     case Pid::SELECTED:
    1197            0 :         return selected();
    1198        18340 :     case Pid::OFFSET:
    1199        18340 :         return PropertyValue::fromValue(m_offset);
    1200         1729 :     case Pid::MIN_DISTANCE:
    1201         1729 :         return m_minDistance;
    1202        75730 :     case Pid::PLACEMENT:
    1203        75730 :         return placement();
    1204        95156 :     case Pid::AUTOPLACE:
    1205        95156 :         return autoplace();
    1206        94888 :     case Pid::Z:
    1207        94888 :         return z();
    1208            8 :     case Pid::SYSTEM_FLAG:
    1209            8 :         return systemFlag();
    1210         1237 :     case Pid::SIZE_SPATIUM_DEPENDENT:
    1211         1237 :         return sizeIsSpatiumDependent();
    1212       104252 :     case Pid::POSITION_LINKED_TO_MASTER:
    1213       104252 :         return _isPositionLinkedToMaster;
    1214       104297 :     case Pid::APPEARANCE_LINKED_TO_MASTER:
    1215       104297 :         return _isAppearanceLinkedToMaster;
    1216        77053 :     case Pid::EXCLUDE_FROM_OTHER_PARTS:
    1217        77053 :         return _excludeFromOtherParts;
    1218          817 :     case Pid::EXCLUDE_VERTICAL_ALIGN:
    1219          817 :         return m_excludeVerticalAlign;
    1220        53411 :     case Pid::HAS_PARENTHESES:
    1221        53411 :         return parenthesesMode();
    1222         3127 :     default:
    1223         3127 :         if (ownershipParent()) {
    1224         3117 :             return ownershipParent()->getProperty(propertyId);
    1225              :         }
    1226              : 
    1227           10 :         return PropertyValue();
    1228              :     }
    1229              : }
    1230              : 
    1231              : //---------------------------------------------------------
    1232              : //   setProperty
    1233              : //---------------------------------------------------------
    1234              : 
    1235       137710 : bool EngravingItem::setProperty(Pid propertyId, const PropertyValue& v)
    1236              : {
    1237       137710 :     switch (propertyId) {
    1238            0 :     case Pid::TRACK:
    1239            0 :         setTrack(v.value<track_idx_t>());
    1240            0 :         break;
    1241            0 :     case Pid::VOICE:
    1242            0 :         setVoice(v.value<voice_idx_t>());
    1243            0 :         break;
    1244           75 :     case Pid::GENERATED:
    1245           75 :         setGenerated(v.toBool());
    1246           75 :         break;
    1247        42148 :     case Pid::COLOR:
    1248        42148 :         setColor(v.value<Color>());
    1249        42148 :         break;
    1250          183 :     case Pid::VISIBLE:
    1251          183 :         setVisible(v.toBool());
    1252          183 :         break;
    1253            0 :     case Pid::SELECTED:
    1254            0 :         setSelected(v.toBool());
    1255            0 :         break;
    1256         1497 :     case Pid::OFFSET:
    1257         1497 :         m_offset = v.value<PointF>();
    1258         1497 :         break;
    1259        11116 :     case Pid::MIN_DISTANCE:
    1260        11116 :         setMinDistance(v.value<Spatium>());
    1261        11116 :         break;
    1262         7611 :     case Pid::PLACEMENT:
    1263         7611 :         setPlacement(v.value<PlacementV>());
    1264         7611 :         break;
    1265           21 :     case Pid::AUTOPLACE:
    1266           21 :         setAutoplace(v.toBool());
    1267           21 :         break;
    1268            0 :     case Pid::Z:
    1269            0 :         setZ(v.toInt());
    1270            0 :         break;
    1271        16451 :     case Pid::SYSTEM_FLAG:
    1272        16451 :         setSystemFlag(v.toBool());
    1273        16451 :         break;
    1274        44112 :     case Pid::SIZE_SPATIUM_DEPENDENT:
    1275        44112 :         setSizeIsSpatiumDependent(v.toBool());
    1276        44112 :         break;
    1277            7 :     case Pid::POSITION_LINKED_TO_MASTER:
    1278            7 :         if (_isPositionLinkedToMaster == v.toBool()) {
    1279            0 :             break;
    1280              :         }
    1281            7 :         if (!_isPositionLinkedToMaster) {
    1282            1 :             relinkPropertiesToMaster(PropertyGroup::POSITION);
    1283              :         }
    1284            7 :         setPositionLinkedToMaster(v.toBool());
    1285            7 :         break;
    1286           39 :     case Pid::APPEARANCE_LINKED_TO_MASTER:
    1287           39 :         if (_isAppearanceLinkedToMaster == v.toBool()) {
    1288           16 :             break;
    1289              :         }
    1290           23 :         if (!_isAppearanceLinkedToMaster) {
    1291            1 :             relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
    1292              :         }
    1293           23 :         setAppearanceLinkedToMaster(v.toBool());
    1294           23 :         break;
    1295           20 :     case Pid::EXCLUDE_FROM_OTHER_PARTS:
    1296           20 :         setExcludeFromOtherParts(v.toBool());
    1297           20 :         break;
    1298            0 :     case Pid::EXCLUDE_VERTICAL_ALIGN:
    1299            0 :         setExcludeVerticalAlign(v.toBool());
    1300            0 :         break;
    1301            1 :     case Pid::HAS_PARENTHESES:
    1302            1 :         setParenthesesMode(v.value<ParenthesesMode>());
    1303            1 :         break;
    1304        14429 :     default:
    1305        14429 :         if (ownershipParent()) {
    1306        14218 :             return ownershipParent()->setProperty(propertyId, v);
    1307              :         }
    1308              : 
    1309              :         LOG_PROP() << typeName() << " unknown <" << propertyName(propertyId) << ">(" << int(propertyId) << "), data: " << v.value<String>();
    1310          211 :         return false;
    1311              :     }
    1312       123281 :     triggerLayout();
    1313       123281 :     return true;
    1314              : }
    1315              : 
    1316            7 : void EngravingItem::manageExclusionFromParts(bool exclude)
    1317              : {
    1318            7 :     if (exclude) {
    1319            4 :         const std::list<EngravingObject*> links = linkList();
    1320           12 :         for (EngravingObject* linkedObject : links) {
    1321            8 :             if (linkedObject->score() == score()) {
    1322            4 :                 continue;
    1323              :             }
    1324            4 :             EngravingItem* linkedItem = toEngravingItem(linkedObject);
    1325            4 :             if (linkedItem->selected()) {
    1326            0 :                 linkedItem->score()->deselect(linkedItem);
    1327              :             }
    1328            4 :             linkedItem->score()->undoRemoveElement(linkedItem, false);
    1329            4 :             linkedItem->undoUnlink();
    1330              :         }
    1331            4 :     } else {
    1332            3 :         score()->undoAddElement(this, /*addToLinkedStaves*/ true, /*ctrlModifier*/ false, this);
    1333              :     }
    1334            7 : }
    1335              : 
    1336        21623 : EngravingItem* EngravingItem::sharedItem() const
    1337              : {
    1338        21623 :     return ldata()->m_sharedItem;
    1339              : }
    1340              : 
    1341        22758 : const std::vector<EngravingItem*>& EngravingItem::originItems() const
    1342              : {
    1343        22758 :     return ldata()->m_originItems;
    1344              : }
    1345              : 
    1346          889 : void EngravingItem::connectSharedItem(EngravingItem* sharedItem, EngravingItem* originItem)
    1347              : {
    1348          889 :     if (originItem->ldata()->m_sharedItem == sharedItem) {
    1349           60 :         return;
    1350              :     }
    1351              : 
    1352          829 :     IF_ASSERT_FAILED(sharedItem->type() == originItem->type()) {
    1353            0 :         return;
    1354              :     }
    1355              : 
    1356          829 :     IF_ASSERT_FAILED(!sharedItem->isChord() && !originItem->isChord()) {
    1357              :         // Notes are connected, not chords
    1358            0 :         return;
    1359              :     }
    1360              : 
    1361          829 :     IF_ASSERT_FAILED(sharedItem->ldata()->m_sharedItem == nullptr && originItem->ldata()->m_originItems.empty()) {
    1362            0 :         return;
    1363              :     }
    1364              : 
    1365          829 :     originItem->mutldata()->m_sharedItem = sharedItem;
    1366              : 
    1367          829 :     std::vector<EngravingItem*>& curOriginItems = sharedItem->mutldata()->m_originItems;
    1368          829 :     auto it = std::lower_bound(curOriginItems.begin(), curOriginItems.end(), originItem, [](EngravingItem* item1, EngravingItem* item2) {
    1369          340 :         return item1->track() < item2->track();
    1370              :     });
    1371              : 
    1372          829 :     curOriginItems.insert(it, originItem);
    1373              : }
    1374              : 
    1375           33 : void EngravingItem::disconnectSharedItem(EngravingItem* sharedItem, EngravingItem* originItem)
    1376              : {
    1377           33 :     IF_ASSERT_FAILED(originItem->m_layoutData->m_sharedItem == sharedItem) {
    1378            0 :         return;
    1379              :     }
    1380              : 
    1381           33 :     originItem->m_layoutData->m_sharedItem = nullptr;
    1382              : 
    1383           33 :     DO_ASSERT(muse::remove(sharedItem->m_layoutData->m_originItems, originItem));
    1384              : }
    1385              : 
    1386        16712 : void EngravingItem::disconnectAllOriginItems(EngravingItem* sharedItem)
    1387              : {
    1388        17205 :     for (EngravingItem* originItem : sharedItem->originItems()) {
    1389          493 :         originItem->m_layoutData->m_sharedItem = nullptr;
    1390              :     }
    1391              : 
    1392        16712 :     sharedItem->m_layoutData->m_originItems.clear();
    1393        16712 : }
    1394              : 
    1395        11936 : void EngravingItem::disconnectSharedTree(EngravingItem* item)
    1396              : {
    1397        15940 :     auto disconnect = [](EngravingItem* item) {
    1398        15940 :         if (EngravingItem* sharedItem = item->sharedItem()) {
    1399           33 :             disconnectSharedItem(sharedItem, item);
    1400              :         }
    1401              : 
    1402        15940 :         disconnectAllOriginItems(item);
    1403        15940 :     };
    1404              : 
    1405        11936 :     if (item->isSpanner()) {
    1406         1028 :         disconnect(item);
    1407         1954 :         for (SpannerSegment* ss : toSpanner(item)->spannerSegments()) {
    1408          926 :             ss->scanElements(disconnect);
    1409              :         }
    1410         1028 :         return;
    1411              :     }
    1412              : 
    1413        10908 :     item->scanElements(disconnect);
    1414              : }
    1415              : 
    1416         4963 : bool EngravingItem::isBefore(const EngravingItem* item) const
    1417              : {
    1418         4963 :     if (!item) {
    1419            0 :         return false;
    1420              :     }
    1421         4963 :     if (tick() != item->tick()) {
    1422         3758 :         return tick() < item->tick();
    1423              :     }
    1424              : 
    1425         1205 :     const Measure* thisMeasure = findMeasure();
    1426         1205 :     const Measure* otherMeasure = item->findMeasure();
    1427         1205 :     if (thisMeasure != otherMeasure) {
    1428          225 :         return thisMeasure->isBefore(otherMeasure);
    1429              :     }
    1430              : 
    1431          980 :     const EngravingItem* thisSeg = findAncestor(ElementType::SEGMENT);
    1432          980 :     const EngravingItem* otherSeg = item->findAncestor(ElementType::SEGMENT);
    1433          980 :     if (!thisSeg || !otherSeg || !thisSeg->isSegment() || !otherSeg->isSegment()) {
    1434            0 :         return false;
    1435              :     }
    1436              : 
    1437          980 :     return toSegment(thisSeg)->goesBefore(toSegment(otherSeg));
    1438              : }
    1439              : 
    1440         2651 : const Staff* EngravingItem::staffToCenterAgainst(bool above, const System* system) const
    1441              : {
    1442         2651 :     const Part* thisPart = part();
    1443         2651 :     const Staff* thisStaff = staff();
    1444         2651 :     if (!thisPart || !thisStaff) {
    1445            0 :         return nullptr;
    1446              :     }
    1447              : 
    1448         2651 :     const Fraction itemTick = tick();
    1449         2651 :     const Instrument* thisPartInstrument = thisPart->instrument(itemTick);
    1450         2651 :     const bool thisInstrumentIsVocal = thisPartInstrument && thisPartInstrument->isVocalInstrument();
    1451         2651 :     const bool participatesInStaffCentering = thisPart->nstaves() > 1 || thisInstrumentIsVocal;
    1452         2651 :     if (!participatesInStaffCentering) {
    1453         1249 :         return nullptr;
    1454              :     }
    1455              : 
    1456         1402 :     if (!system) {
    1457          140 :         if (isSpanner()) {
    1458            6 :             const Segment* startSeg = toSpanner(this)->startSegment();
    1459            6 :             system = startSeg ? startSeg->measure()->system() : nullptr;
    1460              :         } else {
    1461          134 :             system = toSystem(findAncestor(ElementType::SYSTEM));
    1462              :         }
    1463              :     }
    1464              : 
    1465         1402 :     if (!system) {
    1466            0 :         return nullptr;
    1467              :     }
    1468              : 
    1469         1402 :     const staff_idx_t thisIdx = thisStaff->idx();
    1470         1402 :     const staff_idx_t otherIdx = above ? system->prevVisibleStaff(thisIdx) : system->nextVisibleStaff(thisIdx);
    1471         1402 :     if (otherIdx == muse::nidx) {
    1472          515 :         return nullptr;
    1473              :     }
    1474              : 
    1475          887 :     const Staff* otherStaff = score()->staff(otherIdx);
    1476          887 :     if (!otherStaff) {
    1477            0 :         return nullptr;
    1478              :     }
    1479              : 
    1480          887 :     const Part* otherPart = otherStaff->part();
    1481          887 :     if (otherPart == thisPart) {
    1482          477 :         return otherStaff;
    1483              :     }
    1484              : 
    1485          410 :     const Instrument* otherPartInstrument = otherPart->instrument(itemTick);
    1486          410 :     const bool otherInstrumentIsVocal = otherPartInstrument && otherPartInstrument->isVocalInstrument();
    1487              : 
    1488              :     // If the staves are not of the same part, only allow centering if they are both vocal parts:
    1489          410 :     return thisInstrumentIsVocal && otherInstrumentIsVocal ? otherStaff : nullptr;
    1490              : }
    1491              : 
    1492          322 : bool EngravingItem::appliesToAllVoicesInInstrument() const
    1493              : {
    1494          644 :     return hasVoiceAssignmentProperties()
    1495          322 :            && getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>() == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT;
    1496              : }
    1497              : 
    1498            0 : void EngravingItem::setInitialTrackAndVoiceAssignment(track_idx_t track, bool curVoiceOnlyOverride)
    1499              : {
    1500            0 :     IF_ASSERT_FAILED(track != muse::nidx) {
    1501            0 :         return;
    1502              :     }
    1503              : 
    1504            0 :     if (configuration()->dynamicsApplyToAllVoices() && !curVoiceOnlyOverride) {
    1505            0 :         setTrack(trackZeroVoice(track));
    1506            0 :         setProperty(Pid::VOICE_ASSIGNMENT, VoiceAssignment::ALL_VOICE_IN_INSTRUMENT);
    1507              :     } else {
    1508            0 :         setTrack(track);
    1509            0 :         setProperty(Pid::VOICE_ASSIGNMENT, VoiceAssignment::CURRENT_VOICE_ONLY);
    1510              :     }
    1511              : }
    1512              : 
    1513            5 : void EngravingItem::checkVoiceAssignmentCompatibleWithTrack()
    1514              : {
    1515            5 :     voice_idx_t currentVoice = voice();
    1516            5 :     VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
    1517              : 
    1518            5 :     if (voiceAssignment != VoiceAssignment::CURRENT_VOICE_ONLY && currentVoice != 0) {
    1519            0 :         setProperty(Pid::TRACK, trackZeroVoice(track()));
    1520              :     }
    1521            5 : }
    1522              : 
    1523           84 : bool EngravingItem::elementAppliesToTrack(const track_idx_t refTrack) const
    1524              : {
    1525           84 :     if (!hasVoiceAssignmentProperties()) {
    1526           34 :         return refTrack == track();
    1527              :     }
    1528           50 :     const VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
    1529              : 
    1530           50 :     return elementAppliesToTrack(track(), refTrack, voiceAssignment, part());
    1531              : }
    1532              : 
    1533         1717 : void EngravingItem::setPlacementBasedOnVoiceAssignment(DirectionV styledDirection)
    1534              : {
    1535         1717 :     PlacementV oldPlacement = placement();
    1536         1717 :     PlacementV newPlacement = PlacementV::BELOW;
    1537              : 
    1538         1717 :     DirectionV internalDirectionProperty = getProperty(Pid::DIRECTION).value<DirectionV>();
    1539         1717 :     const bool directionIsAuto = internalDirectionProperty == DirectionV::AUTO && styledDirection == DirectionV::AUTO;
    1540         1717 :     const bool centerBetweenStaves = getProperty(Pid::CENTER_BETWEEN_STAVES).value<AutoOnOff>() == AutoOnOff::ON;
    1541         1717 :     const bool gapAbove = directionIsAuto && centerBetweenStaves && staffToCenterAgainst(true);
    1542         1717 :     const bool gapBelow = directionIsAuto && centerBetweenStaves && staffToCenterAgainst(false);
    1543              : 
    1544         1717 :     if (internalDirectionProperty != DirectionV::AUTO) {
    1545          374 :         newPlacement = internalDirectionProperty == DirectionV::UP ? PlacementV::ABOVE : PlacementV::BELOW;
    1546         1343 :     } else if (styledDirection != DirectionV::AUTO) {
    1547            0 :         newPlacement = styledDirection == DirectionV::UP ? PlacementV::ABOVE : PlacementV::BELOW;
    1548         1343 :     } else if (centerBetweenStaves && part()->nstaves() > 1 && (gapAbove || gapBelow)) {
    1549           48 :         bool isOnLastStaffOfInstrument = staffIdx() == part()->staves().back()->idx();
    1550           48 :         newPlacement = isOnLastStaffOfInstrument ? PlacementV::ABOVE : PlacementV::BELOW;
    1551              :     } else {
    1552         1295 :         VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
    1553         1295 :         if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT || voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF) {
    1554         2536 :             if (style().styleB(Sid::dynamicsHairpinsAboveForVocalStaves) && originPart()->instrument(tick())->isVocalInstrument()) {
    1555          180 :                 newPlacement = PlacementV::ABOVE;
    1556              :             } else {
    1557         1088 :                 newPlacement = PlacementV::BELOW;
    1558              :             }
    1559           27 :         } else if (voice() == 0) {
    1560              :             // Put above the staff only in case of multiple voices at this tick (similar to stem directions)
    1561           13 :             const Measure* measure = score()->tick2measure(tick());
    1562           13 :             Fraction startTick = Fraction(-1, 1);
    1563           13 :             Fraction length = Fraction(-1, 1);
    1564           13 :             if (isSpanner()) {
    1565            2 :                 startTick = tick();
    1566            2 :                 length = toSpanner(this)->ticks();
    1567           11 :             } else if (const Segment* segment = toSegment(findAncestor(ElementType::SEGMENT))) {
    1568           11 :                 if (segment && segment->isTimeTickType() && segment->measure() != measure) {
    1569              :                     // Edge case: this is a TimeTick segment at the end of previous measure. Happens only
    1570              :                     // when dynamic is anchorToEndOfPrevious. In this case look for preceding segment.
    1571            0 :                     segment = segment->prev1(SegmentType::Duration);
    1572            0 :                     assert(segment);
    1573            0 :                     measure = segment->measure();
    1574              :                 }
    1575           11 :                 startTick = segment->tick();
    1576           11 :                 length = segment->ticks();
    1577            0 :             } else if (measure) {
    1578            0 :                 startTick = measure->tick();
    1579            0 :                 length = measure->ticks();
    1580              :             }
    1581           13 :             if (measure && measure->hasVoices(staffIdx(), startTick, length)) {
    1582            7 :                 newPlacement = PlacementV::ABOVE;
    1583              :             } else {
    1584            6 :                 newPlacement = PlacementV::BELOW;
    1585              :             }
    1586              :         } else {
    1587           14 :             newPlacement = voice() % 2 ? PlacementV::BELOW : PlacementV::ABOVE;
    1588              :         }
    1589              :     }
    1590              : 
    1591         1717 :     if (directionIsAuto && centerBetweenStaves) {
    1592              :         /* If the preferred side has no staff to center against but the other side has one, place
    1593              :          * the item on that side instead. If neither side has one, leave the placement alone: */
    1594           56 :         const bool preferAbove = newPlacement == PlacementV::ABOVE;
    1595           56 :         const bool preferredGap = preferAbove ? gapAbove : gapBelow;
    1596           56 :         const bool otherGap = preferAbove ? gapBelow : gapAbove;
    1597           56 :         if (!preferredGap && otherGap) {
    1598            5 :             newPlacement = preferAbove ? PlacementV::BELOW : PlacementV::ABOVE;
    1599              :         }
    1600              :     }
    1601              : 
    1602         1717 :     if (newPlacement != oldPlacement) {
    1603          170 :         setPlacement(newPlacement);
    1604              :     }
    1605         1717 : }
    1606              : 
    1607            2 : void EngravingItem::relinkPropertiesToMaster(PropertyGroup propGroup)
    1608              : {
    1609            2 :     assert(!score()->isMaster());
    1610              : 
    1611            2 :     const std::list<EngravingObject*> linkedElements = linkListForPropertyPropagation();
    1612            2 :     EngravingObject* masterElement = nullptr;
    1613            2 :     for (EngravingObject* element : linkedElements) {
    1614            2 :         if (element->score()->isMaster()) {
    1615            2 :             masterElement = element;
    1616            2 :             break;
    1617              :         }
    1618              :     }
    1619              : 
    1620            2 :     if (!masterElement) {
    1621            0 :         return;
    1622              :     }
    1623              : 
    1624          798 :     for (int i = 0; i < static_cast<int>(Pid::END); ++i) {
    1625          796 :         const Pid propertyId = static_cast<Pid>(i);
    1626          796 :         if (propertyGroup(propertyId) != propGroup) {
    1627          445 :             continue;
    1628              :         }
    1629          351 :         const PropertyValue masterValue = masterElement->getProperty(propertyId);
    1630          351 :         const PropertyFlags masterFlags = masterElement->propertyFlags(propertyId);
    1631          351 :         if (getProperty(propertyId) != masterValue) {
    1632            2 :             setProperty(propertyId, masterValue);
    1633              :         }
    1634          351 :         if (propertyFlags(propertyId) != masterFlags) {
    1635            1 :             setPropertyFlags(propertyId, masterFlags);
    1636              :         }
    1637          351 :     }
    1638            2 : }
    1639              : 
    1640            0 : void EngravingItem::relinkPropertyToMaster(Pid propertyId)
    1641              : {
    1642            0 :     assert(!score()->isMaster());
    1643              : 
    1644            0 :     const std::list<EngravingObject*> linkedElements = linkListForPropertyPropagation();
    1645            0 :     EngravingObject* masterElement = nullptr;
    1646            0 :     for (EngravingObject* element : linkedElements) {
    1647            0 :         if (element->score()->isMaster()) {
    1648            0 :             masterElement = element;
    1649            0 :             break;
    1650              :         }
    1651              :     }
    1652              : 
    1653            0 :     if (!masterElement) {
    1654            0 :         return;
    1655              :     }
    1656              : 
    1657            0 :     setProperty(propertyId, masterElement->getProperty(propertyId));
    1658            0 :     setPropertyFlags(propertyId, masterElement->propertyFlags(propertyId));
    1659            0 : }
    1660              : 
    1661          482 : PropertyPropagation EngravingItem::propertyPropagation(const EngravingItem* destinationItem, Pid propertyId) const
    1662              : {
    1663          482 :     assert(destinationItem != this);
    1664              : 
    1665          482 :     if (propertyLink(propertyId)) {
    1666          155 :         return PropertyPropagation::PROPAGATE; // These properties are always linked, no matter what
    1667              :     }
    1668              : 
    1669          327 :     if (propertyGroup(propertyId) == PropertyGroup::NONE) {
    1670          275 :         return PropertyPropagation::NONE;
    1671              :     }
    1672              : 
    1673           52 :     const Score* sourceScore = score();
    1674           52 :     const Score* destinationScore = destinationItem->score();
    1675           52 :     const Staff* sourceStaff = staff();
    1676           52 :     const Staff* destinationStaff = destinationItem->staff();
    1677              : 
    1678           52 :     if (sourceScore == destinationScore) {
    1679            0 :         const bool diffStaff = sourceStaff != destinationStaff;
    1680            0 :         const bool visiblePositionOrColor = propertyId == Pid::VISIBLE || propertyId == Pid::COLOR
    1681            0 :                                             || propertyGroup(propertyId) == PropertyGroup::POSITION;
    1682            0 :         const bool hasParens = propertyId == Pid::HAS_PARENTHESES && isNote() && toNote(this)->ghost()
    1683            0 :                                && !toNote(this)->hideGeneratedParens();
    1684            0 :         const bool linkSameScore = propertyLinkSameScore(propertyId);
    1685            0 :         if ((diffStaff && (visiblePositionOrColor || hasParens)) || !linkSameScore) {
    1686              :             // Allow visibility and position to stay independent
    1687            0 :             return PropertyPropagation::NONE;
    1688              :         }
    1689              :         // Maintain every other property synced
    1690            0 :         return PropertyPropagation::PROPAGATE;
    1691              :     }
    1692              : 
    1693           52 :     const bool isTextProperty = propertyGroup(propertyId) == PropertyGroup::TEXT;
    1694           52 :     if (isTextProperty) {
    1695            2 :         if (sourceScore->isMaster() && destinationItem->isPropertyLinkedToMaster(propertyId)) {
    1696              :             // From master score - check if destination part follows master
    1697            2 :             return PropertyPropagation::PROPAGATE;
    1698              :         }
    1699              : 
    1700            0 :         if (!sourceScore->isMaster() && isPropertyLinkedToMaster(propertyId)) {
    1701              :             // From part - check if source part follows master
    1702            0 :             return PropertyPropagation::PROPAGATE;
    1703              :         }
    1704              :     }
    1705              : 
    1706           50 :     if (!sourceScore->isMaster()) {
    1707              :         // Properties are only propagated when being edited from master. If this is being edited
    1708              :         // from a part score, we mark it as unlinked so it becomes independent in the part.
    1709           34 :         return PropertyPropagation::UNLINK;
    1710              :     }
    1711              : 
    1712           16 :     if (systemFlag() && !isTopSystemObject()) {
    1713              :         // Let only the top system object propagate
    1714            0 :         return PropertyPropagation::NONE;
    1715              :     }
    1716              : 
    1717           16 :     if (destinationItem->isPropertyLinkedToMaster(propertyId)) {
    1718           16 :         return PropertyPropagation::PROPAGATE;
    1719              :     }
    1720              : 
    1721            0 :     return PropertyPropagation::NONE;
    1722              : }
    1723              : 
    1724              : //---------------------------------------------------------
    1725              : //   undoChangeProperty
    1726              : //---------------------------------------------------------
    1727              : 
    1728         6676 : void EngravingItem::undoChangeProperty(Pid pid, const PropertyValue& val, PropertyFlags ps)
    1729              : {
    1730         6676 :     if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
    1731              :         // Switching autoplacement on. Save user-defined
    1732              :         // placement properties to undo stack.
    1733            3 :         undoPushProperty(Pid::PLACEMENT);
    1734            3 :         undoPushProperty(Pid::OFFSET);
    1735              :     }
    1736         6676 :     EngravingObject::undoChangeProperty(pid, val, ps);
    1737         6676 : }
    1738              : 
    1739              : //---------------------------------------------------------
    1740              : //   propertyDefault
    1741              : //---------------------------------------------------------
    1742              : 
    1743       818878 : PropertyValue EngravingItem::propertyDefault(Pid pid) const
    1744              : {
    1745       818878 :     switch (pid) {
    1746            0 :     case Pid::GENERATED:
    1747            0 :         return false;
    1748        95093 :     case Pid::VISIBLE:
    1749        95093 :         return true;
    1750        88113 :     case Pid::COLOR:
    1751        88113 :         return PropertyValue::fromValue(configuration()->defaultColor());
    1752        70710 :     case Pid::PLACEMENT: {
    1753        70710 :         PropertyValue v = EngravingObject::propertyDefault(pid);
    1754        70710 :         if (v.isValid()) {        // if it's a styled property
    1755           26 :             return v;
    1756              :         }
    1757        70684 :         return PlacementV::BELOW;
    1758        70710 :     }
    1759            0 :     case Pid::SELECTED:
    1760            0 :         return false;
    1761        19082 :     case Pid::OFFSET:
    1762        19082 :         return PropertyValue::fromValue(PointF());
    1763         1683 :     case Pid::MIN_DISTANCE: {
    1764         1683 :         PropertyValue v = EngravingObject::propertyDefault(pid);
    1765         1683 :         if (v.isValid()) {
    1766          372 :             return v;
    1767              :         }
    1768         1311 :         return 0.0_sp;
    1769         1683 :     }
    1770        95142 :     case Pid::AUTOPLACE:
    1771        95142 :         return true;
    1772        94886 :     case Pid::Z:
    1773        94886 :         return int(type()) * 100;
    1774        76991 :     case Pid::POSITION_LINKED_TO_MASTER:
    1775        76991 :         return true;
    1776        76991 :     case Pid::APPEARANCE_LINKED_TO_MASTER:
    1777        76991 :         return true;
    1778        76991 :     case Pid::EXCLUDE_FROM_OTHER_PARTS:
    1779        76991 :         return false;
    1780          815 :     case Pid::EXCLUDE_VERTICAL_ALIGN:
    1781          815 :         return false;
    1782        76991 :     case Pid::HAS_PARENTHESES:
    1783       153982 :         return ParenthesesMode::NONE;
    1784        45390 :     default: {
    1785        45390 :         PropertyValue v = EngravingObject::propertyDefault(pid);
    1786              : 
    1787        45390 :         if (v.isValid()) {
    1788        40973 :             return v;
    1789              :         }
    1790              : 
    1791         4417 :         if (ownershipParent()) {
    1792         4063 :             return ownershipParent()->propertyDefault(pid);
    1793              :         }
    1794              : 
    1795          354 :         return PropertyValue();
    1796        45390 :     }
    1797              :     }
    1798              : }
    1799              : 
    1800              : //---------------------------------------------------------
    1801              : //   custom
    1802              : //    check if property is != default
    1803              : //---------------------------------------------------------
    1804              : 
    1805            0 : bool EngravingItem::custom(Pid id) const
    1806              : {
    1807            0 :     return propertyDefault(id) != getProperty(id);
    1808              : }
    1809              : 
    1810              : //---------------------------------------------------------
    1811              : //   isPrintable
    1812              : //---------------------------------------------------------
    1813              : 
    1814            0 : bool EngravingItem::isPrintable() const
    1815              : {
    1816            0 :     switch (type()) {
    1817            0 :     case ElementType::PAGE:
    1818              :     case ElementType::SYSTEM:
    1819              :     case ElementType::MEASURE:
    1820              :     case ElementType::SEGMENT:
    1821              :     case ElementType::VBOX:
    1822              :     case ElementType::HBOX:
    1823              :     case ElementType::TBOX:
    1824              :     case ElementType::FBOX:
    1825              :     case ElementType::SPACER:
    1826              :     case ElementType::SHADOW_NOTE:
    1827              :     case ElementType::LASSO:
    1828            0 :         return false;
    1829            0 :     default:
    1830            0 :         return true;
    1831              :     }
    1832              : }
    1833              : 
    1834            6 : bool EngravingItem::isPlayable() const
    1835              : {
    1836            6 :     switch (type()) {
    1837            3 :     case ElementType::NOTE:
    1838              :     case ElementType::CHORD:
    1839            3 :         return true;
    1840            0 :     case ElementType::HARMONY:
    1841            0 :         return ownershipParent() && ownershipParent()->isSegment();
    1842            3 :     default:
    1843            3 :         return false;
    1844              :     }
    1845              : }
    1846              : 
    1847        80757 : bool EngravingItem::isSystemObjectBelowBottomStaff() const
    1848              : {
    1849        80757 :     return systemFlag() && staff() && staff()->hasSystemObjectsBelowBottomStaff();
    1850              : }
    1851              : 
    1852              : //---------------------------------------------------------
    1853              : //   findAncestor
    1854              : //---------------------------------------------------------
    1855              : 
    1856         1300 : EngravingItem* EngravingItem::findAncestor(ElementType t)
    1857              : {
    1858         1300 :     EngravingItem* e = this;
    1859         4223 :     while (e && e->type() != t) {
    1860         2923 :         e = e->layoutParent();
    1861              :     }
    1862         1300 :     return e;
    1863              : }
    1864              : 
    1865       198821 : const EngravingItem* EngravingItem::findAncestor(ElementType t) const
    1866              : {
    1867       198821 :     const EngravingItem* e = this;
    1868       619544 :     while (e && e->type() != t) {
    1869       420723 :         e = e->layoutParent();
    1870              :     }
    1871       198821 :     return e;
    1872              : }
    1873              : 
    1874              : //---------------------------------------------------------
    1875              : //   findMeasure
    1876              : //---------------------------------------------------------
    1877              : 
    1878       550156 : Measure* EngravingItem::findMeasure()
    1879              : {
    1880       550156 :     if (isMeasure()) {
    1881       174547 :         return toMeasure(this);
    1882       375609 :     } else if (EngravingItem* p = layoutParent()) {
    1883       371660 :         return p->findMeasure();
    1884              :     } else {
    1885         3949 :         return 0;
    1886              :     }
    1887              : }
    1888              : 
    1889              : //---------------------------------------------------------
    1890              : //   findMeasure
    1891              : //---------------------------------------------------------
    1892              : 
    1893       167331 : const Measure* EngravingItem::findMeasure() const
    1894              : {
    1895       167331 :     EngravingItem* e = const_cast<EngravingItem*>(this);
    1896       167331 :     return e->findMeasure();
    1897              : }
    1898              : 
    1899              : //---------------------------------------------------------
    1900              : //   findMeasureBase
    1901              : //---------------------------------------------------------
    1902              : 
    1903        41347 : MeasureBase* EngravingItem::findMeasureBase()
    1904              : {
    1905        41347 :     if (isMeasureBase()) {
    1906        14082 :         return toMeasureBase(this);
    1907        27265 :     } else if (EngravingItem* p = layoutParent()) {
    1908        26624 :         return p->findMeasureBase();
    1909              :     } else {
    1910          641 :         return 0;
    1911              :     }
    1912              : }
    1913              : 
    1914              : //---------------------------------------------------------
    1915              : //   findMeasureBase
    1916              : //---------------------------------------------------------
    1917              : 
    1918        14723 : const MeasureBase* EngravingItem::findMeasureBase() const
    1919              : {
    1920        14723 :     EngravingItem* e = const_cast<EngravingItem*>(this);
    1921        14723 :     return e->findMeasureBase();
    1922              : }
    1923              : 
    1924              : //---------------------------------------------------------
    1925              : //   undoSetVisible
    1926              : //---------------------------------------------------------
    1927              : 
    1928           12 : void EngravingItem::undoSetVisible(bool v)
    1929              : {
    1930           12 :     undoChangeProperty(Pid::VISIBLE, v);
    1931           12 : }
    1932              : 
    1933            0 : void EngravingItem::undoAddElement(EngravingItem* element, bool addToLinkedStaves)
    1934              : {
    1935            0 :     score()->undoAddElement(element, addToLinkedStaves);
    1936            0 : }
    1937              : 
    1938              : //---------------------------------------------------------
    1939              : //   drawSymbol
    1940              : //---------------------------------------------------------
    1941              : 
    1942          231 : void EngravingItem::drawSymbol(SymId id, Painter* p, const PointF& o, double scale) const
    1943              : {
    1944          231 :     score()->engravingFont()->draw(id, p, magS() * scale, o);
    1945          231 : }
    1946              : 
    1947            0 : void EngravingItem::drawSymbols(const SymIdList& symbols, Painter* p, const PointF& o, double scale) const
    1948              : {
    1949            0 :     score()->engravingFont()->draw(symbols, p, magS() * scale, o);
    1950            0 : }
    1951              : 
    1952           20 : void EngravingItem::drawSymbols(const SymIdList& symbols, Painter* p, const PointF& o, const SizeF& scale) const
    1953              : {
    1954           20 :     score()->engravingFont()->draw(symbols, p, SizeF(magS() * scale), o);
    1955           20 : }
    1956              : 
    1957              : //---------------------------------------------------------
    1958              : //   symHeight
    1959              : //---------------------------------------------------------
    1960              : 
    1961         8218 : double EngravingItem::symHeight(SymId id) const
    1962              : {
    1963         8218 :     return score()->engravingFont()->height(id, magS());
    1964              : }
    1965              : 
    1966              : //---------------------------------------------------------
    1967              : //   symWidth
    1968              : //---------------------------------------------------------
    1969              : 
    1970       489259 : double EngravingItem::symWidth(SymId id) const
    1971              : {
    1972       489259 :     return score()->engravingFont()->width(id, magS());
    1973              : }
    1974              : 
    1975            0 : double EngravingItem::symWidth(const SymIdList& symbols) const
    1976              : {
    1977            0 :     return score()->engravingFont()->width(symbols, magS());
    1978              : }
    1979              : 
    1980              : //---------------------------------------------------------
    1981              : //   symAdvance
    1982              : //---------------------------------------------------------
    1983              : 
    1984          114 : double EngravingItem::symAdvance(SymId id) const
    1985              : {
    1986          114 :     return score()->engravingFont()->advance(id, magS());
    1987              : }
    1988              : 
    1989              : //---------------------------------------------------------
    1990              : //   symBbox
    1991              : //---------------------------------------------------------
    1992              : 
    1993       406573 : RectF EngravingItem::symBbox(SymId id) const
    1994              : {
    1995       406573 :     return score()->engravingFont()->bbox(id, magS());
    1996              : }
    1997              : 
    1998         2340 : RectF EngravingItem::symBbox(const SymIdList& symbols) const
    1999              : {
    2000         2340 :     return score()->engravingFont()->bbox(symbols, magS());
    2001              : }
    2002              : 
    2003        42258 : Shape EngravingItem::symShapeWithCutouts(SymId id) const
    2004              : {
    2005        42258 :     Shape shape = score()->engravingFont()->shapeWithCutouts(id, magS());
    2006       174255 :     for (ShapeElement& element : shape.elements()) {
    2007       131997 :         element.setItem(this);
    2008              :     }
    2009              : 
    2010        42258 :     return shape;
    2011              : }
    2012              : 
    2013              : //---------------------------------------------------------
    2014              : //   symSmuflAnchor
    2015              : //---------------------------------------------------------
    2016              : 
    2017       730694 : PointF EngravingItem::symSmuflAnchor(SymId symId, SmuflAnchorId anchorId) const
    2018              : {
    2019       730694 :     return score()->engravingFont()->smuflAnchor(symId, anchorId, magS());
    2020              : }
    2021              : 
    2022              : //---------------------------------------------------------
    2023              : //   symIsValid
    2024              : //---------------------------------------------------------
    2025              : 
    2026            0 : bool EngravingItem::symIsValid(SymId id) const
    2027              : {
    2028            0 :     return score()->engravingFont()->isValid(id);
    2029              : }
    2030              : 
    2031              : //---------------------------------------------------------
    2032              : //   concertPitch
    2033              : //---------------------------------------------------------
    2034              : 
    2035      2163256 : bool EngravingItem::concertPitch() const
    2036              : {
    2037      2163256 :     return style().styleB(Sid::concertPitch);
    2038              : }
    2039              : 
    2040              : //---------------------------------------------------------
    2041              : //   nextElement
    2042              : //   selects the next score element
    2043              : //---------------------------------------------------------
    2044              : 
    2045            1 : EngravingItem* EngravingItem::nextElement()
    2046              : {
    2047            1 :     EngravingItem* e = score()->selection().element();
    2048            1 :     if (!e && !score()->selection().elements().empty()) {
    2049            0 :         e = score()->selection().elements().front();
    2050              :     }
    2051            1 :     if (e) {
    2052            1 :         switch (e->type()) {
    2053            0 :         case ElementType::SEGMENT: {
    2054            0 :             Segment* s = toSegment(e);
    2055            0 :             return s->nextElement(staffIdx());
    2056              :         }
    2057            0 :         case ElementType::MEASURE: {
    2058            0 :             Measure* m = toMeasure(e);
    2059            0 :             return m->nextElementStaff(staffIdx());
    2060              :         }
    2061            1 :         case ElementType::CLEF:
    2062              :         case ElementType::KEYSIG:
    2063              :         case ElementType::TIMESIG:
    2064              :         case ElementType::BAR_LINE:
    2065            1 :             return nextSegmentElement();
    2066            0 :         default: {
    2067            0 :             return e->layoutParent()->nextElement();
    2068              :         }
    2069              :         }
    2070              :     }
    2071            0 :     return nullptr;
    2072              : }
    2073              : 
    2074              : //---------------------------------------------------------
    2075              : //   prevElement
    2076              : //   selects the previous score element
    2077              : //---------------------------------------------------------
    2078              : 
    2079            0 : EngravingItem* EngravingItem::prevElement()
    2080              : {
    2081            0 :     EngravingItem* e = score()->selection().element();
    2082            0 :     if (!e && !score()->selection().elements().empty()) {
    2083            0 :         e = score()->selection().elements().back();
    2084              :     }
    2085            0 :     if (e) {
    2086            0 :         switch (e->type()) {
    2087            0 :         case ElementType::SEGMENT: {
    2088            0 :             Segment* s = toSegment(e);
    2089            0 :             return s->prevElement(staffIdx());
    2090              :         }
    2091            0 :         case ElementType::MEASURE: {
    2092            0 :             Measure* m = toMeasure(e);
    2093            0 :             return m->prevElementStaff(staffIdx());
    2094              :         }
    2095            0 :         case ElementType::CLEF:
    2096              :         case ElementType::KEYSIG:
    2097              :         case ElementType::TIMESIG:
    2098              :         case ElementType::BAR_LINE:
    2099            0 :             return prevSegmentElement();
    2100            0 :         default: {
    2101            0 :             return e->layoutParent()->prevElement();
    2102              :         }
    2103              :         }
    2104              :     }
    2105            0 :     return nullptr;
    2106              : }
    2107              : 
    2108              : //------------------------------------------------------------------------------------------
    2109              : //   nextSegmentElement
    2110              : //   This function is used in for the next-element command to navigate between main elements
    2111              : //   of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
    2112              : //   The default implementation is to look for the first such element. After it is found each
    2113              : //   element knows how to find the next one and overrides this method
    2114              : //------------------------------------------------------------------------------------------
    2115              : 
    2116            0 : EngravingItem* EngravingItem::nextSegmentElement()
    2117              : {
    2118            0 :     EngravingItem* p = this;
    2119            0 :     while (p) {
    2120            0 :         switch (p->type()) {
    2121            0 :         case ElementType::NOTE:
    2122            0 :             if (toNote(p)->chord()->isGrace()) {
    2123            0 :                 break;
    2124              :             }
    2125            0 :             return p;
    2126            0 :         case ElementType::REST:
    2127              :         case ElementType::MMREST:
    2128            0 :             return p;
    2129            0 :         case ElementType::CHORD: {
    2130            0 :             Chord* c = toChord(p);
    2131            0 :             if (!c->isGrace()) {
    2132            0 :                 return c->notes().back();
    2133              :             }
    2134              :         }
    2135            0 :         break;
    2136            0 :         case ElementType::SEGMENT: {
    2137            0 :             Segment* s = toSegment(p);
    2138            0 :             return s->firstElementForNavigation(staffIdx());
    2139              :         }
    2140            0 :         case ElementType::MEASURE: {
    2141            0 :             Measure* m = toMeasure(p);
    2142            0 :             return m->nextElementStaff(staffIdx());
    2143              :         }
    2144            0 :         case ElementType::SYSTEM: {
    2145            0 :             System* sys = toSystem(p);
    2146            0 :             return sys->nextSegmentElement();
    2147              :         }
    2148            0 :         default:
    2149            0 :             break;
    2150              :         }
    2151            0 :         p = p->layoutParent();
    2152              :     }
    2153            0 :     return Navigation::firstElement(score());
    2154              : }
    2155              : 
    2156              : //------------------------------------------------------------------------------------------
    2157              : //   prevSegmentElement
    2158              : //   This function is used in for the prev-element command to navigate between main elements
    2159              : //   of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
    2160              : //   The default implementation is to look for the first such element. After it is found each
    2161              : //   element knows how to find the previous one and overrides this method
    2162              : //------------------------------------------------------------------------------------------
    2163              : 
    2164            0 : EngravingItem* EngravingItem::prevSegmentElement()
    2165              : {
    2166            0 :     EngravingItem* p = this;
    2167            0 :     while (p) {
    2168            0 :         switch (p->type()) {
    2169            0 :         case ElementType::NOTE:
    2170            0 :             if (toNote(p)->chord()->isGrace()) {
    2171            0 :                 break;
    2172              :             }
    2173            0 :             return p;
    2174            0 :         case ElementType::REST:
    2175              :         case ElementType::MMREST:
    2176            0 :             return p;
    2177            0 :         case ElementType::CHORD: {
    2178            0 :             Chord* c = toChord(p);
    2179            0 :             if (!c->isGrace()) {
    2180            0 :                 return c->notes().front();
    2181              :             }
    2182              :         }
    2183            0 :         break;
    2184            0 :         case ElementType::SEGMENT: {
    2185            0 :             Segment* s = toSegment(p);
    2186            0 :             return s->lastElementForNavigation(staffIdx());
    2187              :         }
    2188            0 :         case ElementType::MEASURE: {
    2189            0 :             Measure* m = toMeasure(p);
    2190            0 :             return m->prevElementStaff(staffIdx());
    2191              :         }
    2192            0 :         case ElementType::SYSTEM: {
    2193            0 :             System* sys = toSystem(p);
    2194            0 :             return sys->prevSegmentElement();
    2195              :         }
    2196            0 :         default:
    2197            0 :             break;
    2198              :         }
    2199            0 :         p = p->layoutParent();
    2200              :     }
    2201            0 :     return Navigation::firstElement(score());
    2202              : }
    2203              : 
    2204              : #ifndef ENGRAVING_NO_ACCESSIBILITY
    2205         1884 : AccessibleItemPtr EngravingItem::accessible() const
    2206              : {
    2207         1884 :     return m_accessible;
    2208              : }
    2209              : 
    2210              : #endif
    2211              : 
    2212            0 : String EngravingItem::accessibleInfo() const
    2213              : {
    2214            0 :     return EngravingItem::translatedTypeUserName();
    2215              : }
    2216              : 
    2217              : //---------------------------------------------------------
    2218              : //   nextGrip
    2219              : //---------------------------------------------------------
    2220              : 
    2221            0 : bool EngravingItem::nextGrip(EditData& ed) const
    2222              : {
    2223            0 :     int i = int(ed.curGrip) + 1;
    2224            0 :     if (i >= ed.grips) {
    2225            0 :         ed.curGrip = Grip(0);
    2226            0 :         return false;
    2227              :     }
    2228            0 :     ed.curGrip = Grip(i);
    2229            0 :     return true;
    2230              : }
    2231              : 
    2232              : //---------------------------------------------------------
    2233              : //   prevGrip
    2234              : //---------------------------------------------------------
    2235              : 
    2236            0 : bool EngravingItem::prevGrip(EditData& ed) const
    2237              : {
    2238            0 :     int i = int(ed.curGrip) - 1;
    2239            0 :     if (i < 0) {
    2240            0 :         ed.curGrip = Grip(ed.grips - 1);
    2241            0 :         return false;
    2242              :     }
    2243            0 :     ed.curGrip = Grip(i);
    2244            0 :     return true;
    2245              : }
    2246              : 
    2247              : //---------------------------------------------------------
    2248              : //   isUserModified
    2249              : //    Check if this element was modified by user and
    2250              : //    therefore must be saved.
    2251              : //---------------------------------------------------------
    2252              : 
    2253        17898 : bool EngravingItem::isUserModified() const
    2254              : {
    2255        34594 :     for (const StyledProperty& spp : *styledProperties()) {
    2256        16696 :         Pid pid = spp.pid;
    2257        16696 :         PropertyValue val = getProperty(pid);
    2258        16696 :         PropertyValue defaultValue = propertyDefault(pid);
    2259              : 
    2260        16696 :         if (propertyType(pid) == P_TYPE::ABSOLUTE) {
    2261            0 :             if (std::abs(val.value<double>() - defaultValue.value<double>()) > 0.0001) {         // we don’t care spatium diffs that small
    2262            0 :                 return true;
    2263              :             }
    2264              :         } else {
    2265        16696 :             if (getProperty(pid) != propertyDefault(pid)) {
    2266            0 :                 return true;
    2267              :             }
    2268              :         }
    2269        16696 :     }
    2270       107374 :     for (Pid p : { Pid::VISIBLE, Pid::OFFSET, Pid::COLOR, Pid::Z, Pid::AUTOPLACE }) {
    2271        89479 :         if (getProperty(p) != propertyDefault(p)) {
    2272            3 :             return true;
    2273              :         }
    2274              :     }
    2275        17895 :     return false;
    2276              : }
    2277              : 
    2278              : //---------------------------------------------------------
    2279              : //   triggerLayout
    2280              : //---------------------------------------------------------
    2281              : 
    2282      1162595 : void EngravingItem::triggerLayout() const
    2283              : {
    2284      1162595 :     if (isInScoreTree()) {
    2285       918282 :         score()->setLayout(tick(), staffIdx(), this);
    2286              :     }
    2287      1162595 : }
    2288              : 
    2289              : //----------------------------------------------------------------------
    2290              : //   triggerLayoutAll
    2291              : //
    2292              : //   *************************** CAUTION *******************************
    2293              : //   This causes a layout of the entire score: extremely expensive and
    2294              : //   likely unnecessary! Consider overriding triggerLayout() instead.
    2295              : //----------------------------------------------------------------------
    2296              : 
    2297         3829 : void EngravingItem::triggerLayoutAll() const
    2298              : {
    2299         3829 :     if (isInScoreTree()) {
    2300         2051 :         score()->setLayoutAll(staffIdx(), this);
    2301              :     }
    2302         3829 : }
    2303              : 
    2304          975 : void EngravingItem::triggerLayoutToEnd() const
    2305              : {
    2306          975 :     if (isInScoreTree()) {
    2307          975 :         score()->setLayout(tick(), score()->endTick(), staffIdx(), staffIdx(), this);
    2308              :     }
    2309          975 : }
    2310              : 
    2311              : //---------------------------------------------------------
    2312              : //   addData
    2313              : //---------------------------------------------------------
    2314              : 
    2315            4 : void EditData::addData(std::shared_ptr<ElementEditData> ed)
    2316              : {
    2317            4 :     m_data.push_back(ed);
    2318            4 : }
    2319              : 
    2320              : //---------------------------------------------------------
    2321              : //   startDrag
    2322              : //---------------------------------------------------------
    2323              : 
    2324            0 : void EngravingItem::startDrag(EditData& ed)
    2325              : {
    2326            0 :     if (!isMovable()) {
    2327            0 :         return;
    2328              :     }
    2329            0 :     std::shared_ptr<ElementEditData> eed = std::make_shared<ElementEditData>();
    2330            0 :     eed->e = this;
    2331            0 :     eed->pushProperty(Pid::OFFSET);
    2332            0 :     eed->pushProperty(Pid::AUTOPLACE);
    2333            0 :     ed.addData(eed);
    2334            0 :     if (ed.modifiers & AltModifier) {
    2335            0 :         setAutoplace(false);
    2336              :     }
    2337            0 : }
    2338              : 
    2339              : //---------------------------------------------------------
    2340              : //   drag
    2341              : ///   Return update Rect relative to canvas.
    2342              : //---------------------------------------------------------
    2343              : 
    2344            0 : RectF EngravingItem::drag(EditData& ed)
    2345              : {
    2346            0 :     if (!isMovable()) {
    2347            0 :         return RectF();
    2348              :     }
    2349              : 
    2350            0 :     const RectF r0(canvasBoundingRect());
    2351              : 
    2352            0 :     const PointF offset0 = ed.evtDelta + offset();
    2353            0 :     double x = offset0.x();
    2354            0 :     double y = offset0.y();
    2355              : 
    2356            0 :     double _spatium = spatium();
    2357            0 :     if (ed.hRaster) {
    2358            0 :         double hRaster = _spatium / MScore::hRaster();
    2359            0 :         int n = lrint(x / hRaster);
    2360            0 :         x = hRaster * n;
    2361              :     }
    2362            0 :     if (ed.vRaster) {
    2363            0 :         double vRaster = _spatium / MScore::vRaster();
    2364            0 :         int n = lrint(y / vRaster);
    2365            0 :         y = vRaster * n;
    2366              :     }
    2367              : 
    2368            0 :     setOffset(PointF(x, y));
    2369              : //      setGenerated(false);
    2370              : 
    2371            0 :     if (isTextBase()) {           // TODO: check for other types
    2372              :         //
    2373              :         // restrict move to page boundaries
    2374              :         //
    2375            0 :         const RectF r(canvasBoundingRect());
    2376            0 :         Page* p = 0;
    2377            0 :         EngravingItem* e = this;
    2378            0 :         while (e) {
    2379            0 :             if (e->isPage()) {
    2380            0 :                 p = toPage(e);
    2381            0 :                 break;
    2382              :             }
    2383            0 :             e = e->layoutParent();
    2384              :         }
    2385            0 :         if (p) {
    2386            0 :             bool move = false;
    2387            0 :             RectF pr(p->canvasBoundingRect());
    2388            0 :             if (r.right() > pr.right()) {
    2389            0 :                 x -= r.right() - pr.right();
    2390            0 :                 move = true;
    2391            0 :             } else if (r.left() < pr.left()) {
    2392            0 :                 x += pr.left() - r.left();
    2393            0 :                 move = true;
    2394              :             }
    2395            0 :             if (r.bottom() > pr.bottom()) {
    2396            0 :                 y -= r.bottom() - pr.bottom();
    2397            0 :                 move = true;
    2398            0 :             } else if (r.top() < pr.top()) {
    2399            0 :                 y += pr.top() - r.top();
    2400            0 :                 move = true;
    2401              :             }
    2402            0 :             if (move) {
    2403            0 :                 setOffset(PointF(x, y));
    2404              :             }
    2405              :         }
    2406              :     }
    2407            0 :     return canvasBoundingRect().united(r0);
    2408              : }
    2409              : 
    2410              : //---------------------------------------------------------
    2411              : //   endDrag
    2412              : //---------------------------------------------------------
    2413              : 
    2414            0 : void EngravingItem::endDrag(EditData& ed)
    2415              : {
    2416            0 :     if (!isMovable()) {
    2417            0 :         return;
    2418              :     }
    2419            0 :     ElementEditDataPtr eed = ed.getData(this);
    2420            0 :     if (!eed) {
    2421            0 :         return;
    2422              :     }
    2423            0 :     for (const auto& [id, v, f] : eed->propertyData) {
    2424            0 :         setPropertyFlags(id, f);     // reset initial property flags state
    2425            0 :         score()->undoPropertyChanged(this, id, v,
    2426            0 :                                      f == PropertyFlags::STYLED ? PropertyFlags::UNSTYLED : f);
    2427            0 :         setGenerated(false);
    2428              :     }
    2429            0 :     score()->hideAnchors();
    2430            0 : }
    2431              : 
    2432              : //---------------------------------------------------------
    2433              : //   genericDragAnchorLines
    2434              : //---------------------------------------------------------
    2435              : 
    2436            0 : std::vector<LineF> EngravingItem::genericDragAnchorLines() const
    2437              : {
    2438            0 :     double xp = 0.0;
    2439            0 :     for (EngravingItem* e = layoutParent(); e; e = e->layoutParent()) {
    2440            0 :         xp += e->x();
    2441              :     }
    2442              :     double yp;
    2443            0 :     if (ownershipParent()->isSegment() || ownershipParent()->isMeasure()) {
    2444            0 :         Measure* meas = ownershipParent()->isSegment() ? toSegment(ownershipParent())->measure() : toMeasure(ownershipParent());
    2445            0 :         System* system = meas->system();
    2446            0 :         const staff_idx_t stIdx = effectiveStaffIdx();
    2447            0 :         if (stIdx == muse::nidx) {
    2448            0 :             return { LineF() };
    2449              :         }
    2450            0 :         yp = system ? system->staffCanvasYpage(stIdx) : 0.0;
    2451            0 :         if (placement() == PlacementV::BELOW) {
    2452            0 :             yp += system ? system->staff(stIdx)->bbox().height() : 0.0;
    2453              :         }
    2454              :         //adjust anchor Y positions to staffType offset
    2455            0 :         if (staff()) {
    2456            0 :             yp += staff()->staffTypeForElement(this)->yoffset().val() * spatium();
    2457              :         }
    2458              :     } else {
    2459            0 :         yp = layoutParent()->canvasPos().y();
    2460              :     }
    2461            0 :     PointF p1(xp, yp);
    2462            0 :     LineF anchorLine(p1, canvasPos());
    2463            0 :     return { anchorLine };
    2464              : }
    2465              : 
    2466              : //---------------------------------------------------------
    2467              : //   updateGrips
    2468              : //---------------------------------------------------------
    2469              : 
    2470            0 : void EngravingItem::updateGrips(EditData& ed) const
    2471              : {
    2472            0 :     const auto positions(gripsPositions(ed));
    2473            0 :     const size_t ngrips = positions.size();
    2474            0 :     for (int i = 0; i < int(ngrips); ++i) {
    2475            0 :         ed.grip[i].translate(positions[i]);
    2476              :     }
    2477            0 : }
    2478              : 
    2479              : //---------------------------------------------------------
    2480              : //   startEdit
    2481              : //---------------------------------------------------------
    2482              : 
    2483            0 : void EngravingItem::startEdit(EditData& ed)
    2484              : {
    2485            0 :     std::shared_ptr<ElementEditData> elementData = std::make_shared<ElementEditData>();
    2486            0 :     elementData->e = this;
    2487            0 :     ed.addData(elementData);
    2488            0 : }
    2489              : 
    2490              : //---------------------------------------------------------
    2491              : //   isEditAllowed
    2492              : //---------------------------------------------------------
    2493              : 
    2494            0 : bool EngravingItem::isEditAllowed(EditData& ed) const
    2495              : {
    2496            0 :     return ed.key == Key_Home;
    2497              : }
    2498              : 
    2499              : //---------------------------------------------------------
    2500              : //   edit
    2501              : //    return true if event is accepted
    2502              : //---------------------------------------------------------
    2503              : 
    2504            0 : bool EngravingItem::edit(EditData& ed)
    2505              : {
    2506            0 :     if (ed.key == Key_Home) {
    2507            0 :         setOffset(PointF());
    2508            0 :         return true;
    2509              :     }
    2510            0 :     return false;
    2511              : }
    2512              : 
    2513              : //---------------------------------------------------------
    2514              : //   startDragGrip
    2515              : //---------------------------------------------------------
    2516              : 
    2517            0 : void EngravingItem::startDragGrip(EditData& ed)
    2518              : {
    2519            0 :     ElementEditDataPtr eed = ed.getData(this);
    2520            0 :     if (!eed) {
    2521            0 :         eed = std::make_shared<ElementEditData>();
    2522            0 :         eed->e = this;
    2523            0 :         ed.addData(eed);
    2524              :     }
    2525            0 :     eed->pushProperty(Pid::OFFSET);
    2526            0 :     eed->pushProperty(Pid::AUTOPLACE);
    2527            0 :     if (ed.modifiers & AltModifier) {
    2528            0 :         setAutoplace(false);
    2529              :     }
    2530            0 : }
    2531              : 
    2532              : //---------------------------------------------------------
    2533              : //   dragGrip
    2534              : //---------------------------------------------------------
    2535              : 
    2536            0 : void EngravingItem::dragGrip(EditData& ed)
    2537              : {
    2538            0 :     IF_ASSERT_FAILED(ed.curGrip != Grip::NO_GRIP) {
    2539            0 :         return;
    2540              :     }
    2541              : 
    2542            0 :     score()->addRefresh(canvasBoundingRect());
    2543            0 :     setOffset(offset() + ed.delta);
    2544            0 :     score()->addRefresh(canvasBoundingRect());
    2545              : }
    2546              : 
    2547              : //---------------------------------------------------------
    2548              : //   endDragGrip
    2549              : //---------------------------------------------------------
    2550              : 
    2551            0 : void EngravingItem::endDragGrip(EditData& ed)
    2552              : {
    2553            0 :     ElementEditDataPtr eed = ed.getData(this);
    2554            0 :     bool changed = false;
    2555            0 :     if (eed) {
    2556            0 :         for (const auto& [id, data, flags] : eed->propertyData) {
    2557            0 :             setPropertyFlags(id, flags);       // reset initial property flags state
    2558            0 :             PropertyFlags f = flags;
    2559            0 :             if (f == PropertyFlags::STYLED) {
    2560            0 :                 f = PropertyFlags::UNSTYLED;
    2561              :             }
    2562            0 :             if (score()->undoPropertyChanged(this, id, data, f)) {
    2563            0 :                 changed = true;
    2564              :             }
    2565              :         }
    2566            0 :         eed->propertyData.clear();
    2567              :     }
    2568            0 :     if (changed) {
    2569            0 :         undoChangeProperty(Pid::GENERATED, false);
    2570              :     }
    2571            0 :     score()->hideAnchors();
    2572            0 : }
    2573              : 
    2574              : //---------------------------------------------------------
    2575              : //   endEdit
    2576              : //---------------------------------------------------------
    2577              : 
    2578            0 : void EngravingItem::endEdit(EditData&)
    2579              : {
    2580            0 :     score()->hideAnchors();
    2581            0 : }
    2582              : 
    2583              : //---------------------------------------------------------
    2584              : //   styleP
    2585              : //---------------------------------------------------------
    2586              : 
    2587         6693 : double EngravingItem::styleP(Sid idx) const
    2588              : {
    2589         6693 :     return style().styleAbsolute(idx);
    2590              : }
    2591              : 
    2592          257 : void EngravingItem::setParenthesesMode(const ParenthesesMode& v, bool addToLinked, bool generated)
    2593              : {
    2594          257 :     setHasLeftParenthesis(v & ParenthesesMode::LEFT, addToLinked, generated);
    2595          257 :     setHasRightParenthesis(v & ParenthesesMode::RIGHT, addToLinked, generated);
    2596          257 : }
    2597              : 
    2598        53498 : ParenthesesMode EngravingItem::parenthesesMode() const
    2599              : {
    2600        53498 :     ParenthesesMode p = ParenthesesMode::NONE;
    2601        53498 :     if (m_leftParenthesis) {
    2602            0 :         p |= ParenthesesMode::LEFT;
    2603              :     }
    2604        53498 :     if (m_rightParenthesis) {
    2605            0 :         p |= ParenthesesMode::RIGHT;
    2606              :     }
    2607              : 
    2608        53498 :     return p;
    2609              : }
    2610              : 
    2611          257 : void EngravingItem::setHasLeftParenthesis(bool v, bool addToLinked, bool generated)
    2612              : {
    2613          257 :     const bool hasGeneratedParen = m_leftParenthesis && m_leftParenthesis->generated();
    2614          257 :     const bool hasUserParen = m_leftParenthesis && !m_leftParenthesis->generated();
    2615              : 
    2616          257 :     if (generated && v == hasGeneratedParen) {
    2617           64 :         return;
    2618              :     }
    2619              : 
    2620          193 :     if (!generated && v == hasUserParen) {
    2621          174 :         return;
    2622              :     }
    2623              : 
    2624           19 :     if (v) {
    2625           19 :         if (!m_leftParenthesis) {
    2626           19 :             Parenthesis* paren = Factory::createParenthesis(this);
    2627           19 :             paren->setTrack(track());
    2628           19 :             paren->setDirection(DirectionH::LEFT);
    2629           19 :             paren->setGenerated(generated);
    2630              : 
    2631           19 :             score()->undoAddElement(paren, addToLinked);
    2632              :         }
    2633              :     } else {
    2634            0 :         score()->undoRemoveElement(m_leftParenthesis, addToLinked);
    2635            0 :         assert(m_leftParenthesis == nullptr);
    2636              :     }
    2637              : }
    2638              : 
    2639          257 : void EngravingItem::setHasRightParenthesis(bool v, bool addToLinked, bool generated)
    2640              : {
    2641          257 :     const bool hasGeneratedParen = m_rightParenthesis && m_rightParenthesis->generated();
    2642          257 :     const bool hasUserParen = m_rightParenthesis && !m_rightParenthesis->generated();
    2643              : 
    2644          257 :     if (generated && v == hasGeneratedParen) {
    2645           64 :         return;
    2646              :     }
    2647              : 
    2648          193 :     if (!generated && v == hasUserParen) {
    2649          174 :         return;
    2650              :     }
    2651              : 
    2652           19 :     if (v) {
    2653           19 :         if (!m_rightParenthesis) {
    2654           19 :             Parenthesis* paren = Factory::createParenthesis(this);
    2655           19 :             paren->setTrack(track());
    2656           19 :             paren->setDirection(DirectionH::RIGHT);
    2657           19 :             paren->setGenerated(generated);
    2658              : 
    2659           19 :             score()->undoAddElement(paren, addToLinked);
    2660              :         }
    2661              :     } else {
    2662            0 :         score()->undoRemoveElement(m_rightParenthesis, addToLinked);
    2663            0 :         assert(m_rightParenthesis == nullptr);
    2664              :     }
    2665              : }
    2666              : 
    2667           35 : EngravingItem::BarBeat EngravingItem::barbeat() const
    2668              : {
    2669           35 :     EngravingItem::BarBeat barBeat = { 0, 0, 0.0F };
    2670           35 :     const EngravingItem* parent = this;
    2671           82 :     while (parent && !parent->isSegment() && !parent->isMeasure()) {
    2672           47 :         parent = parent->layoutParent();
    2673              :     }
    2674              : 
    2675           35 :     if (!parent) {
    2676            0 :         return barBeat;
    2677              :     }
    2678              : 
    2679           35 :     int bar = 0;
    2680           35 :     int displayedBar = 0;
    2681           35 :     int beat = 0;
    2682           35 :     int ticks = 0;
    2683           35 :     const Measure* measure = nullptr;
    2684              : 
    2685           35 :     const TimeSigMap* timeSigMap = score()->sigmap();
    2686           35 :     int ticksB = ticks_beat(timeSigMap->timesig(0).timesig().denominator());
    2687              : 
    2688           35 :     if (parent->isSegment()) {
    2689           35 :         const Segment* segment = toSegment(parent);
    2690           35 :         timeSigMap->tickValues(segment->tick().ticks(), &bar, &beat, &ticks);
    2691           35 :         ticksB = ticks_beat(timeSigMap->timesig(segment->tick().ticks()).timesig().denominator());
    2692           35 :         measure = segment->findMeasure();
    2693           35 :         if (measure) {
    2694           35 :             displayedBar = measure->measureNumber();
    2695              :         }
    2696            0 :     } else if (parent->isMeasure()) {
    2697            0 :         measure = toMeasure(parent);
    2698            0 :         bar = measure->measureNumber();
    2699            0 :         displayedBar = bar;
    2700            0 :         beat = -1;
    2701            0 :         ticks = 0;
    2702              :     }
    2703              : 
    2704           35 :     barBeat = { bar + 1, displayedBar + 1, beat + 1 + ticks / static_cast<float>(ticksB) };
    2705           35 :     return barBeat;
    2706              : }
    2707              : 
    2708           32 : EngravingItem* EngravingItem::findLinkedInScore(const Score* score) const
    2709              : {
    2710           32 :     if (!score || !staff() || !links() || links()->empty()) {
    2711           16 :         return nullptr;
    2712              :     }
    2713              : 
    2714              :     // If this is a system element it may not be on the same stave, so just check if linked elements are in the score
    2715           16 :     if (systemFlag() && track() == 0) {
    2716            0 :         for (EngravingObject* linked : *links()) {
    2717            0 :             if (linked != this && linked->score() == score) {
    2718            0 :                 return toEngravingItem(linked);
    2719              :             }
    2720              :         }
    2721            0 :         return nullptr;
    2722              :     }
    2723              : 
    2724           16 :     Staff* linkedStaffInScore = staff()->findLinkedInScore(score);
    2725              : 
    2726           16 :     if (!linkedStaffInScore) {
    2727           12 :         return nullptr;
    2728              :     }
    2729              : 
    2730            4 :     return findLinkedInStaff(linkedStaffInScore);
    2731              : }
    2732              : 
    2733         1609 : EngravingItem* EngravingItem::findLinkedInStaff(const Staff* staff) const
    2734              : {
    2735         1609 :     if (!links() || links()->empty()) {
    2736          484 :         return nullptr;
    2737              :     }
    2738              : 
    2739         2294 :     for (EngravingObject* linked : *links()) {
    2740         2293 :         if (toEngravingItem(linked)->staff() == staff) {
    2741         1124 :             return toEngravingItem(linked);
    2742              :         }
    2743              :     }
    2744            1 :     return nullptr;
    2745              : }
    2746              : 
    2747        47234 : bool EngravingItem::selected() const
    2748              : {
    2749        47234 :     return flag(ElementFlag::SELECTED);
    2750              : }
    2751              : 
    2752       321928 : void EngravingItem::setSelected(bool f)
    2753              : {
    2754       321928 :     setFlag(ElementFlag::SELECTED, f);
    2755       321928 : }
    2756              : 
    2757      1252489 : void EngravingItem::setVisible(bool f)
    2758              : {
    2759      1252489 :     setFlag(ElementFlag::INVISIBLE, !f);
    2760      1252489 :     if (m_leftParenthesis) {
    2761            0 :         m_leftParenthesis->setVisible(f);
    2762              :     }
    2763      1252489 :     if (m_rightParenthesis) {
    2764            0 :         m_rightParenthesis->setVisible(f);
    2765              :     }
    2766      1252489 : }
    2767              : 
    2768              : #ifndef ENGRAVING_NO_ACCESSIBILITY
    2769         1883 : void EngravingItem::initAccessibleIfNeed()
    2770              : {
    2771         1883 :     if (!contextConfiguration() || !contextConfiguration()->isAccessibleEnabled()) {
    2772         1883 :         return;
    2773              :     }
    2774              : 
    2775            0 :     if (!m_accessibleEnabled) {
    2776            0 :         return;
    2777              :     }
    2778              : 
    2779            0 :     doInitAccessible();
    2780              : }
    2781              : 
    2782            0 : void EngravingItem::doInitAccessible()
    2783              : {
    2784            0 :     EngravingItemList parents;
    2785            0 :     EngravingItem* parent = accessibleParentItem();
    2786            0 :     while (parent) {
    2787            0 :         parents.push_back(parent);
    2788            0 :         parent = parent->accessibleParentItem();
    2789              :     }
    2790              : 
    2791            0 :     for (auto it = parents.rbegin(); it != parents.rend(); ++it) {
    2792            0 :         (*it)->setupAccessible();
    2793              :     }
    2794              : 
    2795            0 :     setupAccessible();
    2796            0 : }
    2797              : 
    2798              : #endif // ENGRAVING_NO_ACCESSIBILITY
    2799              : 
    2800            0 : String EngravingItem::formatBarsAndBeats() const
    2801              : {
    2802            0 :     String result;
    2803            0 :     EngravingItem::BarBeat barbeat = this->barbeat();
    2804              : 
    2805            0 :     if (barbeat.bar != 0) {
    2806            0 :         result = muse::mtrc("engraving", "Measure: %1").arg(barbeat.bar);
    2807              : 
    2808            0 :         if (barbeat.displayedBar != barbeat.bar) {
    2809            0 :             result += u"; " + muse::mtrc("engraving", "Displayed measure: %1").arg(barbeat.displayedBar);
    2810              :         }
    2811              : 
    2812            0 :         if (!muse::RealIsNull(barbeat.beat)) {
    2813            0 :             result += u"; " + muse::mtrc("engraving", "Beat: %1").arg(barbeat.beat);
    2814              :         }
    2815              :     }
    2816              : 
    2817            0 :     return result;
    2818            0 : }
    2819              : 
    2820           19 : bool EngravingItem::isPropertyLinkedToMaster(Pid id) const
    2821              : {
    2822           19 :     if (propertyGroup(id) == PropertyGroup::POSITION) {
    2823            0 :         return isPositionLinkedToMaster();
    2824              :     }
    2825              : 
    2826           19 :     if (propertyGroup(id) == PropertyGroup::APPEARANCE) {
    2827           19 :         return isAppearanceLinkedToMaster();
    2828              :     }
    2829              : 
    2830            0 :     return true;
    2831              : }
    2832              : 
    2833            0 : bool EngravingItem::isUnlinkedFromMaster() const
    2834              : {
    2835            0 :     return !(getProperty(Pid::POSITION_LINKED_TO_MASTER).toBool()
    2836            0 :              && getProperty(Pid::APPEARANCE_LINKED_TO_MASTER).toBool());
    2837              : }
    2838              : 
    2839           34 : void EngravingItem::unlinkPropertyFromMaster(Pid id)
    2840              : {
    2841           34 :     if (propertyGroup(id) == PropertyGroup::POSITION) {
    2842            1 :         score()->undo(new ChangeProperty(this, Pid::POSITION_LINKED_TO_MASTER, false));
    2843           33 :     } else if (propertyGroup(id) == PropertyGroup::APPEARANCE) {
    2844           33 :         score()->undo(new ChangeProperty(this, Pid::APPEARANCE_LINKED_TO_MASTER, false));
    2845              :     }
    2846           34 : }
    2847              : 
    2848       182805 : EngravingItem::LayoutData* EngravingItem::createLayoutData() const
    2849              : {
    2850       182805 :     return new EngravingItem::LayoutData();
    2851              : }
    2852              : 
    2853     32877825 : const EngravingItem::LayoutData* EngravingItem::ldata() const
    2854              : {
    2855     32877825 :     return ldataInternal();
    2856              : }
    2857              : 
    2858      5344126 : EngravingItem::LayoutData* EngravingItem::mutldata()
    2859              : {
    2860      5344126 :     return mutldataInternal();
    2861              : }
    2862              : 
    2863     32877825 : const EngravingItem::LayoutData* EngravingItem::ldataInternal() const
    2864              : {
    2865     32877825 :     if (!m_layoutData) {
    2866       315571 :         m_layoutData = createLayoutData();
    2867       315571 :         m_layoutData->m_item = this;
    2868              :     }
    2869     32877825 :     return m_layoutData;
    2870              : }
    2871              : 
    2872      5344126 : EngravingItem::LayoutData* EngravingItem::mutldataInternal()
    2873              : {
    2874      5344126 :     if (!m_layoutData) {
    2875       311789 :         m_layoutData = createLayoutData();
    2876       311789 :         m_layoutData->m_item = this;
    2877              :     }
    2878      5344126 :     return m_layoutData;
    2879              : }
    2880              : 
    2881           67 : bool EngravingItem::elementAppliesToTrack(const track_idx_t elementTrack, const track_idx_t refTrack,
    2882              :                                           const VoiceAssignment voiceAssignment, const Part* part)
    2883              : {
    2884           67 :     if (voiceAssignment == VoiceAssignment::CURRENT_VOICE_ONLY && elementTrack == refTrack) {
    2885            3 :         return true;
    2886              :     }
    2887              : 
    2888           64 :     if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF && track2staff(elementTrack) == track2staff(refTrack)) {
    2889            0 :         return true;
    2890              :     }
    2891              : 
    2892           64 :     if (!part) {
    2893            0 :         return false;
    2894              :     }
    2895              : 
    2896           64 :     if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT) {
    2897           49 :         const TrackRange range = part->trackRange();
    2898           49 :         if (range.startTrack <= refTrack && range.endTrack - 1 >= refTrack) {
    2899           10 :             return true;
    2900              :         }
    2901              :     }
    2902           54 :     return false;
    2903              : }
    2904              : 
    2905      1697723 : void EngravingItem::LayoutData::setBbox(const RectF& r)
    2906              : {
    2907      1697723 :     DO_ASSERT(!std::isnan(r.x()) && !std::isinf(r.x()));
    2908      1697723 :     DO_ASSERT(!std::isnan(r.y()) && !std::isinf(r.y()));
    2909      1697723 :     DO_ASSERT(!std::isnan(r.width()) && !std::isinf(r.width()));
    2910      1697723 :     DO_ASSERT(!std::isnan(r.height()) && !std::isinf(r.height()));
    2911              : 
    2912              :     //DO_ASSERT(!isShapeComposite());
    2913      1697723 :     m_shape.set_value(Shape(r, m_item, Shape::Type::Fixed));
    2914      1697723 : }
    2915              : 
    2916          137 : void EngravingItem::LayoutData::connectItemSnappedBefore(EngravingItem* itemBefore)
    2917              : {
    2918          137 :     IF_ASSERT_FAILED(itemBefore && itemBefore != m_item && itemBefore != m_itemSnappedAfter
    2919              :                      && itemBefore->ldata()->itemSnappedBefore() != m_item) {
    2920            0 :         return;
    2921              :     }
    2922          137 :     m_itemSnappedBefore = itemBefore;
    2923          137 :     itemBefore->mutldata()->m_itemSnappedAfter = const_cast<EngravingItem*>(m_item);
    2924              : }
    2925              : 
    2926         2880 : void EngravingItem::LayoutData::disconnectItemSnappedBefore()
    2927              : {
    2928         2880 :     if (!m_itemSnappedBefore) {
    2929         2863 :         return;
    2930              :     }
    2931           17 :     m_itemSnappedBefore->mutldata()->m_itemSnappedAfter = nullptr;
    2932           17 :     m_itemSnappedBefore = nullptr;
    2933              : }
    2934              : 
    2935           73 : void EngravingItem::LayoutData::connectItemSnappedAfter(EngravingItem* itemAfter)
    2936              : {
    2937           73 :     IF_ASSERT_FAILED(itemAfter && itemAfter != m_item && itemAfter != m_itemSnappedBefore
    2938              :                      && itemAfter->ldata()->itemSnappedAfter() != m_item) {
    2939            0 :         return;
    2940              :     }
    2941           73 :     m_itemSnappedAfter = itemAfter;
    2942           73 :     itemAfter->mutldata()->m_itemSnappedBefore = const_cast<EngravingItem*>(m_item);
    2943              : }
    2944              : 
    2945         2880 : void EngravingItem::LayoutData::disconnectItemSnappedAfter()
    2946              : {
    2947         2880 :     if (!m_itemSnappedAfter) {
    2948         2841 :         return;
    2949              :     }
    2950           39 :     m_itemSnappedAfter->mutldata()->m_itemSnappedBefore = nullptr;
    2951           39 :     m_itemSnappedAfter = nullptr;
    2952              : }
    2953              : 
    2954         5983 : void EngravingItem::LayoutData::scanConnectedItemsInSnappingChain(const System* system,
    2955              :                                                                   const std::function<void(EngravingItem*)>& func) const
    2956              : {
    2957         5983 :     EngravingItem* item = const_cast<EngravingItem*>(m_item);
    2958         5983 :     func(item);
    2959              : 
    2960        11966 :     std::vector<EngravingItem*> visited = { item }; // To break out of circular snap chains
    2961              : 
    2962         5983 :     EngravingItem* snappedBefore = m_itemSnappedBefore;
    2963         6172 :     while (snappedBefore && snappedBefore->findAncestor(ElementType::SYSTEM) == system) {
    2964          189 :         IF_ASSERT_FAILED(!muse::contains(visited, snappedBefore)) {
    2965            0 :             break;
    2966              :         }
    2967          189 :         visited.push_back(snappedBefore);
    2968          189 :         func(snappedBefore);
    2969          189 :         snappedBefore = snappedBefore->ldata()->itemSnappedBefore();
    2970              :     }
    2971              : 
    2972         5983 :     EngravingItem* snappedAfter = m_itemSnappedAfter;
    2973         6386 :     while (snappedAfter && snappedAfter->findAncestor(ElementType::SYSTEM) == system) {
    2974          403 :         IF_ASSERT_FAILED(!muse::contains(visited, snappedAfter)) {
    2975            0 :             break;
    2976              :         }
    2977          403 :         visited.push_back(snappedAfter);
    2978          403 :         func(snappedAfter);
    2979          403 :         snappedAfter = snappedAfter->ldata()->itemSnappedAfter();
    2980              :     }
    2981         5983 : }
    2982              : 
    2983      2877748 : const RectF& EngravingItem::LayoutData::bbox(LD_ACCESS mode) const
    2984              : {
    2985              :     //! NOTE Temporary disabled CHECK - a lot of messages
    2986              :     UNUSED(mode);
    2987      2877748 :     const Shape& sh = m_shape.value(LD_ACCESS::MAYBE_NOTINITED);
    2988              : 
    2989              :     //! NOTE Temporary
    2990              :     {
    2991              :         static const RectF _dummy;
    2992              : 
    2993      2877748 :         switch (m_item->type()) {
    2994       177326 :         case ElementType::NOTE:
    2995              :         case ElementType::ORNAMENT:
    2996       177326 :             return !sh.elements().empty() ? sh.elements().at(0) : _dummy;
    2997      2700422 :         default:
    2998      2700422 :             break;
    2999              :         }
    3000              :     }
    3001              : 
    3002      2700422 :     return sh.bbox();
    3003              : }
    3004              : 
    3005      2931490 : Shape EngravingItem::LayoutData::shape(LD_ACCESS mode) const
    3006              : {
    3007              :     //! NOTE Temporary disabled CHECK - a lot of messages
    3008      2931490 :     Shape sh = m_shape.value(LD_ACCESS::MAYBE_NOTINITED);
    3009              : 
    3010      2931490 :     const Parenthesis* leftParen = m_item->leftParen();
    3011      2931490 :     if (leftParen && leftParen->addToSkyline()) {
    3012          118 :         sh.add(leftParen->ldata()->shape().translated(leftParen->pos()));
    3013              :     }
    3014      2931490 :     const Parenthesis* rightParen = m_item->rightParen();
    3015      2931490 :     if (rightParen && rightParen->addToSkyline()) {
    3016          118 :         sh.add(rightParen->ldata()->shape().translated(rightParen->pos()));
    3017              :     }
    3018              : 
    3019              :     //! NOTE Temporary
    3020              :     //! Reimplementation: done
    3021              :     {
    3022      2931490 :         switch (m_item->type()) {
    3023      1133574 :         case ElementType::BEAM:
    3024              :         case ElementType::GRACE_NOTES_GROUP:
    3025              :         case ElementType::ORNAMENT:
    3026              :         case ElementType::OTTAVA_SEGMENT:
    3027              :         case ElementType::VOLTA_SEGMENT:
    3028              :         case ElementType::PEDAL_SEGMENT:
    3029              :         case ElementType::TEXTLINE_SEGMENT:
    3030              :         case ElementType::HARMONIC_MARK_SEGMENT:
    3031              :         case ElementType::PALM_MUTE_SEGMENT:
    3032              :         case ElementType::LET_RING_SEGMENT:
    3033              :         case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
    3034              :         case ElementType::RASGUEADO_SEGMENT:
    3035              :         case ElementType::WHAMMY_BAR_SEGMENT:
    3036              :         case ElementType::SLUR_SEGMENT:
    3037              :         case ElementType::TIE_SEGMENT:
    3038              :         case ElementType::LAISSEZ_VIB_SEGMENT:
    3039              :         case ElementType::PARTIAL_TIE_SEGMENT:
    3040              :         case ElementType::NOTE:
    3041      1133574 :             return sh;
    3042       447769 :         case ElementType::CHORD:
    3043              :         case ElementType::REST:
    3044              :         case ElementType::MEASURE_REPEAT:
    3045              :         case ElementType::MMREST: {
    3046       447769 :             if (mode == LD_ACCESS::CHECK) {
    3047              :                 //! NOTE Temporary fix
    3048              :                 //! We can remove it the moment we figure out the layout order of the elements
    3049       443929 :                 LayoutContext ctx(m_item->score());
    3050       443929 :                 ChordRest::LayoutData* ldata = static_cast<ChordRest::LayoutData*>(const_cast<LayoutData*>(this));
    3051       443929 :                 ChordLayout::checkAndFillShape(toChordRest(m_item), ldata, ctx.conf());
    3052       443929 :             }
    3053       447769 :             return m_shape.value(LD_ACCESS::CHECK);
    3054              :         } break;
    3055         1323 :         case ElementType::GUITAR_BEND_SEGMENT: {
    3056              :             //! NOTE Temporary fix
    3057              :             //! We can remove it the moment we figure out the layout order of the elements
    3058         1323 :             TLayout::fillGuitarBendSegmentShape(toGuitarBendSegment(m_item),
    3059              :                                                 static_cast<GuitarBendSegment::LayoutData*>(const_cast<LayoutData*>(this)));
    3060         1323 :             return m_shape.value(LD_ACCESS::CHECK);
    3061              :         } break;
    3062         1056 :         case ElementType::HAIRPIN_SEGMENT: {
    3063              :             //! To be removed when we're confident enough...
    3064         1056 :             IF_ASSERT_FAILED(m_shape.has_value()) {
    3065            0 :                 const_cast<LayoutData*>(this)->setShape(TLayout::recalculateTextLineBaseSegmentShape(toHairpinSegment(m_item)));
    3066              :             }
    3067         1056 :             return m_shape.value(LD_ACCESS::CHECK);
    3068              :         } break;
    3069          232 :         case ElementType::TRILL_SEGMENT: {
    3070              :             //! NOTE Temporary fix
    3071              :             //! We can remove it the moment we figure out the layout order of the elements
    3072          232 :             LayoutContext ctx(m_item->score());
    3073          232 :             TLayout::fillTrillSegmentShape(toTrillSegment(m_item),
    3074              :                                            static_cast<HairpinSegment::LayoutData*>(const_cast<LayoutData*>(this)),
    3075              :                                            ctx.conf());
    3076          232 :             return m_shape.value(LD_ACCESS::CHECK);
    3077          232 :         } break;
    3078         1941 :         case ElementType::TUPLET: {
    3079              :             //! NOTE Temporary fix
    3080              :             //! We can remove it the moment we figure out the layout order of the elements
    3081         1941 :             TLayout::fillTupletShape(toTuplet(m_item), static_cast<Tuplet::LayoutData*>(const_cast<LayoutData*>(this)));
    3082         1941 :             return m_shape.value(LD_ACCESS::CHECK);
    3083              :         } break;
    3084      1345595 :         default:
    3085      1345595 :             break;
    3086              :         }
    3087              :     }
    3088              : 
    3089      1345595 :     return sh;
    3090      2931490 : }
    3091              : 
    3092              : #ifndef NDEBUG
    3093      2538272 : void EngravingItem::LayoutData::doSetPosDebugHook(double x, double y)
    3094              : {
    3095              :     UNUSED(x);
    3096              :     UNUSED(y);
    3097      2538272 : }
    3098              : 
    3099       778731 : void EngravingItem::LayoutData::setWidthDebugHook(double w)
    3100              : {
    3101              :     UNUSED(w);
    3102       778731 : }
    3103              : 
    3104              : #endif
    3105              : 
    3106            0 : void EngravingItem::LayoutData::dump(std::stringstream& ss) const
    3107              : {
    3108            0 :     ss << "\n";
    3109            0 :     ss << m_item->typeName() << " id: " << m_item->eid().toStdString() << "\n";
    3110              : 
    3111            0 :     ss << "skip: " << (m_isSkipDraw ? "yes" : "no") << "\n";
    3112            0 :     ss << "mag: " << m_mag << "\n";
    3113              : 
    3114            0 :     ss << "pos: ";
    3115            0 :     mu::engraving::dump(m_pos, ss);
    3116            0 :     ss << "\n";
    3117              : 
    3118            0 :     ss << "shape: ";
    3119            0 :     mu::engraving::dump(m_shape, ss);
    3120            0 :     ss << "\n";
    3121              : 
    3122            0 :     supDump(ss);
    3123            0 : }
    3124              : 
    3125      4958683 : double EngravingItem::mag() const
    3126              : {
    3127      4958683 :     if (!ldata()) {
    3128              :         //LOGD() << "no layout data, will be returned default (1.0)";
    3129            0 :         return 1.0;
    3130              :     }
    3131      4958683 :     return ldata()->mag();
    3132              : }
    3133              : 
    3134       710510 : PointF EngravingItem::staffOffset() const
    3135              : {
    3136       710510 :     const StaffType* st = staffType();
    3137       710510 :     const double yOffset = st ? st->yoffset().val() * spatium() : 0.0;
    3138       710510 :     return PointF(0.0, yOffset);
    3139              : }
    3140              : }
        

Generated by: LCOV version 2.0-1