LCOV - code coverage report
Current view: top level - src/engraving/dom - page.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 63.2 % 136 86
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "page.h"
      24              : 
      25              : #ifndef ENGRAVING_NO_ACCESSIBILITY
      26              : #include "accessibility/accessibleitem.h"
      27              : #endif
      28              : 
      29              : #include "factory.h"
      30              : #include "measurebase.h"
      31              : #include "mscore.h"
      32              : #include "rootitem.h"
      33              : #include "score.h"
      34              : #include "system.h"
      35              : 
      36              : using namespace mu::engraving;
      37              : 
      38              : //---------------------------------------------------------
      39              : //   Page
      40              : //---------------------------------------------------------
      41              : 
      42         3554 : Page::Page(Score* parent)
      43         3554 :     : EngravingItem(ElementType::PAGE, parent, ElementFlag::NOT_SELECTABLE)
      44              : {
      45         3554 :     m_bspTreeValid = false;
      46         3554 : }
      47              : 
      48            0 : void Page::setOwnershipParent(Score* score)
      49              : {
      50            0 :     EngravingItem::setOwnershipParent(score);
      51            0 : }
      52              : 
      53       126770 : EngravingItem* Page::layoutParent() const
      54              : {
      55              :     // A page is the top of the visual hierarchy: it is not laid out inside anything.
      56       126770 :     return nullptr;
      57              : }
      58              : 
      59            3 : EngravingItem* Page::accessibleParentItem() const
      60              : {
      61              :     // Above the visual hierarchy sits the root item, which exists only to head the
      62              :     // accessibility tree. A page that is not in the score belongs to the dummy's tree,
      63              :     // which the base implementation finds.
      64            3 :     return ownershipParent() ? score()->rootItem() : EngravingItem::accessibleParentItem();
      65              : }
      66              : 
      67         6624 : Page::~Page()
      68              : {
      69         3315 :     for (System* s : m_systems) {
      70            3 :         if (s->page() == this) {
      71            0 :             s->setPage(nullptr);
      72              :         }
      73              :     }
      74         6624 : }
      75              : 
      76        10228 : MeasureBase* Page::firstMeasureBase() const
      77              : {
      78        10228 :     if (m_systems.empty()) {
      79            0 :         return nullptr;
      80              :     }
      81        10228 :     System* firstSys = m_systems.front();
      82        10228 :     return firstSys ? firstSys->first() : nullptr;
      83              : }
      84              : 
      85            7 : MeasureBase* Page::lastMeasureBase() const
      86              : {
      87            7 :     if (m_systems.empty()) {
      88            0 :         return nullptr;
      89              :     }
      90            7 :     System* lastSys = m_systems.back();
      91            7 :     return lastSys ? lastSys->last() : nullptr;
      92              : }
      93              : 
      94              : //---------------------------------------------------------
      95              : //   items
      96              : //---------------------------------------------------------
      97              : 
      98            5 : std::vector<EngravingItem*> Page::items(const RectF& rect)
      99              : {
     100            5 :     if (!m_bspTreeValid) {
     101            5 :         doRebuildBspTree();
     102              :     }
     103            5 :     return bspTree.items(rect);
     104              : }
     105              : 
     106            0 : std::vector<EngravingItem*> Page::items(const PointF& point)
     107              : {
     108            0 :     if (!m_bspTreeValid) {
     109            0 :         doRebuildBspTree();
     110              :     }
     111            0 :     return bspTree.items(point);
     112              : }
     113              : 
     114              : //---------------------------------------------------------
     115              : //   appendSystem
     116              : //---------------------------------------------------------
     117              : 
     118        10053 : void Page::appendSystem(System* s)
     119              : {
     120        10053 :     s->setPage(this);
     121        10053 :     m_systems.push_back(s);
     122        10053 : }
     123              : 
     124            4 : EngravingItemList Page::accessibleChildren() const
     125              : {
     126              :     // The systems are owned by the score, but it is the page that places them, so the
     127              :     // page is where the accessibility tree finds them.
     128            4 :     EngravingItemList children = EngravingItem::accessibleChildren();
     129            4 :     children.insert(children.end(), m_systems.begin(), m_systems.end());
     130              : 
     131            4 :     return children;
     132            0 : }
     133              : 
     134              : #ifndef ENGRAVING_NO_ACCESSIBILITY
     135            0 : AccessibleItemPtr Page::createAccessible()
     136              : {
     137            0 :     return std::make_shared<AccessibleItem>(this, AccessibleItem::Group);
     138              : }
     139              : 
     140              : #endif
     141              : 
     142              : //---------------------------------------------------------
     143              : //   scanElements
     144              : //---------------------------------------------------------
     145              : 
     146           14 : void Page::scanElements(std::function<void(EngravingItem*)> func)
     147              : {
     148           40 :     for (System* s :m_systems) {
     149          166 :         for (MeasureBase* m : s->measures()) {
     150          140 :             m->scanElements(func);
     151              :         }
     152           26 :         s->scanElements(func);
     153              :     }
     154              : 
     155           14 :     func(this);
     156              : 
     157           56 :     for (int i = 0; i < MAX_HEADERS; i++) {
     158           42 :         if (Text* t = headerText(i)) {
     159            0 :             func(t);
     160              :         }
     161              :     }
     162           56 :     for (int i = 0; i < MAX_FOOTERS; i++) {
     163           42 :         if (Text* t = footerText(i)) {
     164            0 :             func(t);
     165              :         }
     166              :     }
     167           14 : }
     168              : 
     169              : //---------------------------------------------------------
     170              : //   doRebuildBspTree
     171              : //---------------------------------------------------------
     172              : 
     173            5 : void Page::doRebuildBspTree()
     174              : {
     175            5 :     int n = 0;
     176          390 :     auto countElements = [&](EngravingItem* item) {
     177          390 :         if (item->collectForDrawing()) {
     178          390 :             ++n;
     179              :         }
     180          395 :     };
     181              : 
     182            5 :     scanElements(countElements);
     183              : 
     184            5 :     RectF r;
     185            5 :     if (score()->linearMode()) {
     186            0 :         double w = 0.0;
     187            0 :         double h = 0.0;
     188            0 :         if (!m_systems.empty()) {
     189            0 :             h = m_systems.front()->height();
     190            0 :             if (!m_systems.front()->measures().empty()) {
     191            0 :                 MeasureBase* mb = m_systems.front()->measures().back();
     192            0 :                 w = mb->x() + mb->width();
     193              :             }
     194              :         }
     195            0 :         r = RectF(0.0, 0.0, w, h);
     196              :     } else {
     197            5 :         r = pageBoundingRect();
     198              :     }
     199              : 
     200            5 :     bspTree.initialize(r, n);
     201              : 
     202          390 :     auto bspInsert = [&] (EngravingItem* item) {
     203          390 :         if (item->collectForDrawing()) {
     204          390 :             bspTree.insert(item);
     205              :         }
     206          395 :     };
     207            5 :     scanElements(bspInsert);
     208              : 
     209            5 :     m_bspTreeValid = true;
     210            5 : }
     211              : 
     212              : //---------------------------------------------------------
     213              : //   getDisplayPageNumber
     214              : //---------------------------------------------------------
     215              : 
     216        33490 : int Page::getDisplayPageNumber() const
     217              : {
     218        33490 :     return static_cast<int>(m_pageNumber) + 1 + score()->pageNumberOffset();
     219              : }
     220              : 
     221              : //---------------------------------------------------------
     222              : //   isOdd
     223              : //---------------------------------------------------------
     224              : 
     225        28428 : bool Page::isOdd() const
     226              : {
     227        28428 :     return getDisplayPageNumber() & 1;
     228              : }
     229              : 
     230              : //---------------------------------------------------------
     231              : //   elements
     232              : //---------------------------------------------------------
     233              : 
     234            4 : std::vector<EngravingItem*> Page::elements() const
     235              : {
     236            4 :     return getChildren(false);
     237              : }
     238              : 
     239              : //---------------------------------------------------------
     240              : //   tm
     241              : //---------------------------------------------------------
     242              : 
     243         7600 : double Page::tm() const
     244              : {
     245        15049 :     return ((!style().styleB(Sid::pageTwosided) || isOdd())
     246        15049 :             ? style().styleD(Sid::pageOddTopMargin) : style().styleD(Sid::pageEvenTopMargin)) * DPI;
     247              : }
     248              : 
     249              : //---------------------------------------------------------
     250              : //   bm
     251              : //---------------------------------------------------------
     252              : 
     253        10585 : double Page::bm() const
     254              : {
     255        20987 :     return ((!style().styleB(Sid::pageTwosided) || isOdd())
     256        20987 :             ? style().styleD(Sid::pageOddBottomMargin) : style().styleD(Sid::pageEvenBottomMargin)) * DPI;
     257              : }
     258              : 
     259              : //---------------------------------------------------------
     260              : //   lm
     261              : //---------------------------------------------------------
     262              : 
     263        10800 : double Page::lm() const
     264              : {
     265        21377 :     return ((!style().styleB(Sid::pageTwosided) || isOdd())
     266        21377 :             ? style().styleD(Sid::pageOddLeftMargin) : style().styleD(Sid::pageEvenLeftMargin)) * DPI;
     267              : }
     268              : 
     269              : //---------------------------------------------------------
     270              : //   rm
     271              : //---------------------------------------------------------
     272              : 
     273          240 : double Page::rm() const
     274              : {
     275          240 :     return (style().styleD(Sid::pageWidth) - style().styleD(Sid::pagePrintableWidth)) * DPI - lm();
     276              : }
     277              : 
     278              : //---------------------------------------------------------
     279              : //   tbbox
     280              : //    calculates and returns smallest rectangle containing all (visible) page elements
     281              : //---------------------------------------------------------
     282              : 
     283            0 : RectF Page::tbbox() const
     284              : {
     285            0 :     double x1 = width();
     286            0 :     double x2 = 0.0;
     287            0 :     double y1 = height();
     288            0 :     double y2 = 0.0;
     289            0 :     const std::vector<EngravingItem*> el = elements();
     290            0 :     for (EngravingItem* e : el) {
     291            0 :         if (e == this || !e->isPrintable()) {
     292            0 :             continue;
     293              :         }
     294            0 :         RectF ebbox = e->pageBoundingRect();
     295            0 :         if (ebbox.left() < x1) {
     296            0 :             x1 = ebbox.left();
     297              :         }
     298            0 :         if (ebbox.right() > x2) {
     299            0 :             x2 = ebbox.right();
     300              :         }
     301            0 :         if (ebbox.top() < y1) {
     302            0 :             y1 = ebbox.top();
     303              :         }
     304            0 :         if (ebbox.bottom() > y2) {
     305            0 :             y2 = ebbox.bottom();
     306              :         }
     307              :     }
     308            0 :     if (x1 < x2 && y1 < y2) {
     309            0 :         return RectF(x1, y1, x2 - x1, y2 - y1);
     310              :     } else {
     311            0 :         return pageBoundingRect();
     312              :     }
     313            0 : }
     314              : 
     315              : //---------------------------------------------------------
     316              : //   endTick
     317              : //---------------------------------------------------------
     318              : 
     319          643 : Fraction Page::endTick() const
     320              : {
     321          643 :     return m_systems.empty() ? Fraction(-1, 1) : m_systems.back()->measures().back()->endTick();
     322              : }
     323              : 
     324            0 : Measure* Page::firstMeasure() const
     325              : {
     326            0 :     for (System* s : m_systems) {
     327            0 :         if (Measure* m = s->firstMeasure()) {
     328            0 :             return m;
     329              :         }
     330              :     }
     331              : 
     332            0 :     return nullptr;
     333              : }
     334              : 
     335           18 : bool Page::isLocked() const
     336              : {
     337           18 :     MeasureBase* firstMeasure = firstMeasureBase();
     338           18 :     return firstMeasure ? firstMeasure->isStartOfPageLock() : false;
     339              : }
     340              : 
     341        10195 : const RangeLock* Page::pageLock() const
     342              : {
     343        10195 :     MeasureBase* firstMeasure = firstMeasureBase();
     344        10195 :     return firstMeasure ? firstMeasure->pageLock() : nullptr;
     345              : }
        

Generated by: LCOV version 2.0-1