LCOV - code coverage report
Current view: top level - src/engraving/dom - box.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 60.4 % 623 376
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 "box.h"
      24              : 
      25              : #include <cmath>
      26              : 
      27              : #include "../editing/elementeditdata.h"
      28              : #include "../editing/editfretboarddiagram.h"
      29              : 
      30              : #include "actionicon.h"
      31              : #include "factory.h"
      32              : #include "fret.h"
      33              : #include "harmony.h"
      34              : #include "layoutbreak.h"
      35              : #include "masterscore.h"
      36              : #include "measure.h"
      37              : #include "mscore.h"
      38              : #include "score.h"
      39              : #include "stafftext.h"
      40              : #include "system.h"
      41              : #include "text.h"
      42              : 
      43              : #include "log.h"
      44              : 
      45              : using namespace mu;
      46              : using namespace mu::engraving;
      47              : using namespace muse::draw;
      48              : 
      49              : namespace mu::engraving {
      50              : static const ElementStyle boxStyle {
      51              :     { Sid::systemFrameDistance,                Pid::TOP_GAP },
      52              :     { Sid::frameSystemDistance,                Pid::BOTTOM_GAP },
      53              :     { Sid::paddingToNotationAbove,             Pid::PADDING_TO_NOTATION_ABOVE },
      54              :     { Sid::paddingToNotationBelow,             Pid::PADDING_TO_NOTATION_BELOW },
      55              : };
      56              : 
      57              : static const ElementStyle hBoxStyle {
      58              : };
      59              : 
      60              : static const String FRET_BOX_DIAGRAMS_SEPARATOR = u",";
      61              : 
      62         1423 : Box::Box(const ElementType& type, Score* parent)
      63         1423 :     : MeasureBase(type, parent)
      64              : {
      65         1423 : }
      66              : 
      67              : //---------------------------------------------------------
      68              : //   computeMinWidth
      69              : //---------------------------------------------------------
      70              : 
      71          358 : void HBox::computeMinWidth()
      72              : {
      73          358 :     setWidth(absoluteFromSpatium(boxWidth() + topGap() + bottomGap()));    // top/bottom is really left/right
      74          358 : }
      75              : 
      76            0 : bool Box::isEditAllowed(EditData&) const
      77              : {
      78            0 :     return false;
      79              : }
      80              : 
      81              : //---------------------------------------------------------
      82              : //   edit
      83              : //---------------------------------------------------------
      84              : 
      85            0 : bool Box::edit(EditData&)
      86              : {
      87            0 :     return false;
      88              : }
      89              : 
      90              : //---------------------------------------------------------
      91              : //   startDragGrip
      92              : //---------------------------------------------------------
      93              : 
      94            0 : void Box::startDragGrip(EditData& ed)
      95              : {
      96            0 :     ElementEditDataPtr eed = ed.getData(this);
      97            0 :     if (isHBox()) {
      98            0 :         eed->pushProperty(Pid::BOX_WIDTH);
      99              :     } else {
     100            0 :         eed->pushProperty(Pid::BOX_HEIGHT);
     101              :     }
     102            0 : }
     103              : 
     104            0 : void Box::dragGrip(EditData& ed)
     105              : {
     106            0 :     const double sp = sizeIsSpatiumDependent() ? spatium() : style().defaultSpatium();
     107            0 :     if (isVBox()) {
     108            0 :         m_boxHeight += Spatium(ed.delta.y() / sp);
     109            0 :         if (ed.vRaster) {
     110            0 :             double vRaster = 1.0 / MScore::vRaster();
     111            0 :             int n = lrint(m_boxHeight.val() / vRaster);
     112            0 :             m_boxHeight = Spatium(vRaster * n);
     113              :         }
     114            0 :         mutldata()->setBbox(0.0, 0.0, system()->width(), absoluteFromSpatium(boxHeight()));
     115            0 :         system()->setHeight(height());
     116              :     } else {
     117            0 :         m_boxWidth += Spatium(ed.delta.x() / sp);
     118            0 :         if (ed.hRaster) {
     119            0 :             double hRaster = 1.0 / MScore::hRaster();
     120            0 :             int n = lrint(m_boxWidth.val() / hRaster);
     121            0 :             m_boxWidth = Spatium(hRaster * n);
     122              :         }
     123              :     }
     124            0 :     triggerLayout();
     125            0 : }
     126              : 
     127              : //---------------------------------------------------------
     128              : //   gripsPositions
     129              : //---------------------------------------------------------
     130              : 
     131            0 : std::vector<PointF> HBox::gripsPositions(const EditData&) const
     132              : {
     133            0 :     RectF r(pageBoundingRect());
     134            0 :     return { PointF(r.right(), r.top() + r.height() * .5) };
     135              : }
     136              : 
     137            0 : std::vector<PointF> VBox::gripsPositions(const EditData&) const
     138              : {
     139            0 :     RectF r(pageBoundingRect());
     140            0 :     return { PointF(r.x() + r.width() * .5, r.bottom()) };
     141              : }
     142              : 
     143              : //---------------------------------------------------------
     144              : //   add
     145              : ///   Add new EngravingItem \a el to Box
     146              : //---------------------------------------------------------
     147              : 
     148         2363 : void Box::add(EngravingItem* e)
     149              : {
     150         2363 :     if (e->isText()) {
     151         2331 :         toText(e)->setLayoutToParentWidth(true);
     152              :     }
     153         2363 :     MeasureBase::add(e);
     154         2363 : }
     155              : 
     156        14882 : double Box::absoluteFromSpatium(const Spatium& val) const
     157              : {
     158        14882 :     const double sp = sizeIsSpatiumDependent() ? spatium() : style().defaultSpatium();
     159        14882 :     return val.val() * sp;
     160              : }
     161              : 
     162         1504 : RectF Box::contentRect() const
     163              : {
     164         1504 :     RectF result;
     165              : 
     166         4339 :     for (const EngravingItem* element : el()) {
     167         2835 :         result = result.united(element->ldata()->bbox());
     168              :     }
     169              : 
     170         1504 :     return result;
     171              : }
     172              : 
     173              : //---------------------------------------------------------
     174              : //   getProperty
     175              : //---------------------------------------------------------
     176              : 
     177        10603 : PropertyValue Box::getProperty(Pid propertyId) const
     178              : {
     179        10603 :     switch (propertyId) {
     180          653 :     case Pid::BOX_HEIGHT:
     181          653 :         return m_boxHeight;
     182          653 :     case Pid::BOX_WIDTH:
     183          653 :         return m_boxWidth;
     184           31 :     case Pid::TOP_GAP:
     185           31 :         return m_topGap;
     186           33 :     case Pid::BOTTOM_GAP:
     187           33 :         return m_bottomGap;
     188          653 :     case Pid::LEFT_MARGIN:
     189          653 :         return m_leftMargin;
     190          653 :     case Pid::RIGHT_MARGIN:
     191          653 :         return m_rightMargin;
     192          665 :     case Pid::TOP_MARGIN:
     193          665 :         return m_topMargin;
     194          665 :     case Pid::BOTTOM_MARGIN:
     195          665 :         return m_bottomMargin;
     196           30 :     case Pid::BOX_AUTOSIZE:
     197           30 :         return (score()->mscVersion() >= 302) ? m_isAutoSizeEnabled : false;
     198         6567 :     default:
     199         6567 :         return MeasureBase::getProperty(propertyId);
     200              :     }
     201              : }
     202              : 
     203              : //---------------------------------------------------------
     204              : //   setProperty
     205              : //---------------------------------------------------------
     206              : 
     207         4540 : bool Box::setProperty(Pid propertyId, const PropertyValue& v)
     208              : {
     209         4540 :     switch (propertyId) {
     210         1438 :     case Pid::BOX_HEIGHT:
     211         1438 :         m_boxHeight = v.value<Spatium>();
     212         1438 :         break;
     213           87 :     case Pid::BOX_WIDTH:
     214           87 :         m_boxWidth = v.value<Spatium>();
     215           87 :         break;
     216         1410 :     case Pid::TOP_GAP:
     217         1410 :         m_topGap = v.value<Spatium>();
     218         1410 :         break;
     219         1409 :     case Pid::BOTTOM_GAP:
     220         1409 :         m_bottomGap = v.value<Spatium>();
     221         1409 :         break;
     222           10 :     case Pid::LEFT_MARGIN:
     223           10 :         m_leftMargin = v.toDouble();
     224           10 :         break;
     225           10 :     case Pid::RIGHT_MARGIN:
     226           10 :         m_rightMargin = v.toDouble();
     227           10 :         break;
     228            5 :     case Pid::TOP_MARGIN:
     229            5 :         m_topMargin = v.toDouble();
     230            5 :         break;
     231            5 :     case Pid::BOTTOM_MARGIN:
     232            5 :         m_bottomMargin = v.toDouble();
     233            5 :         break;
     234            0 :     case Pid::BOX_AUTOSIZE:
     235            0 :         m_isAutoSizeEnabled = v.toBool();
     236            0 :         break;
     237          166 :     default:
     238          166 :         return MeasureBase::setProperty(propertyId, v);
     239              :     }
     240         4374 :     triggerLayout();
     241         4374 :     return true;
     242              : }
     243              : 
     244              : //---------------------------------------------------------
     245              : //   propertyDefault
     246              : //---------------------------------------------------------
     247              : 
     248        10846 : PropertyValue Box::propertyDefault(Pid id) const
     249              : {
     250        10846 :     switch (id) {
     251          653 :     case Pid::BOX_HEIGHT:
     252              :     case Pid::BOX_WIDTH:
     253          653 :         return 0.0_sp;
     254              : 
     255           52 :     case Pid::TOP_GAP:
     256           52 :         return isHBox() ? 0.0_sp : style().styleS(Sid::systemFrameDistance);
     257           53 :     case Pid::BOTTOM_GAP:
     258           53 :         return isHBox() ? 0.0_sp : style().styleS(Sid::frameSystemDistance);
     259              : 
     260         2642 :     case Pid::LEFT_MARGIN:
     261              :     case Pid::RIGHT_MARGIN:
     262              :     case Pid::TOP_MARGIN:
     263              :     case Pid::BOTTOM_MARGIN:
     264         2642 :         return 0.0;
     265          653 :     case Pid::BOX_AUTOSIZE:
     266          653 :         return true;
     267          902 :     case Pid::SIZE_SPATIUM_DEPENDENT:
     268          902 :         return !isTitleFrame();
     269         5891 :     default:
     270         5891 :         return MeasureBase::propertyDefault(id);
     271              :     }
     272              : }
     273              : 
     274          993 : bool Box::isTitleFrame() const
     275              : {
     276          993 :     return this == score()->first() && isVBox();
     277              : }
     278              : 
     279              : //---------------------------------------------------------
     280              : //   copyValues
     281              : //---------------------------------------------------------
     282              : 
     283           39 : void Box::copyValues(Box* origin)
     284              : {
     285           39 :     m_boxHeight    = origin->boxHeight();
     286           39 :     m_boxWidth     = origin->boxWidth();
     287              : 
     288           39 :     double factor  = magS() / origin->magS();
     289           39 :     m_bottomGap    = origin->bottomGap() * factor;
     290           39 :     m_topGap       = origin->topGap() * factor;
     291           39 :     m_bottomMargin = origin->bottomMargin() * factor;
     292           39 :     m_topMargin    = origin->topMargin() * factor;
     293           39 :     m_leftMargin   = origin->leftMargin() * factor;
     294           39 :     m_rightMargin  = origin->rightMargin() * factor;
     295              : 
     296           39 :     setSizeIsSpatiumDependent(origin->sizeIsSpatiumDependent());
     297           39 : }
     298              : 
     299              : //---------------------------------------------------------
     300              : //   HBox
     301              : //---------------------------------------------------------
     302              : 
     303           87 : HBox::HBox(Score* parent)
     304           87 :     : Box(ElementType::HBOX, parent)
     305              : {
     306           87 :     initElementStyle(&hBoxStyle);
     307           87 :     resetProperty(Pid::BOX_WIDTH);
     308           87 : }
     309              : 
     310              : //---------------------------------------------------------
     311              : //   acceptDrop
     312              : //---------------------------------------------------------
     313              : 
     314            0 : bool Box::acceptDrop(EditData& data) const
     315              : {
     316            0 :     if (data.dropElement->flag(ElementFlag::ON_STAFF)) {
     317            0 :         return false;
     318              :     }
     319            0 :     if (MScore::debugMode) {
     320            0 :         LOGD("<%s>", data.dropElement->typeName());
     321              :     }
     322            0 :     ElementType t = data.dropElement->type();
     323            0 :     switch (t) {
     324            0 :     case ElementType::TEXT:
     325              :     case ElementType::STAFF_TEXT:
     326              :     case ElementType::IMAGE:
     327              :     case ElementType::SYMBOL:
     328            0 :         return true;
     329            0 :     case ElementType::ACTION_ICON:
     330            0 :         switch (toActionIcon(data.dropElement)->actionType()) {
     331            0 :         case ActionIconType::VFRAME:
     332              :         case ActionIconType::TFRAME:
     333              :         case ActionIconType::FFRAME:
     334              :         case ActionIconType::HFRAME:
     335              :         case ActionIconType::MEASURE:
     336            0 :             return true;
     337            0 :         default:
     338            0 :             break;
     339              :         }
     340            0 :         break;
     341            0 :     case ElementType::BAR_LINE:
     342            0 :         return isHBox();
     343            0 :     default:
     344            0 :         break;
     345              :     }
     346            0 :     return MeasureBase::acceptDrop(data);
     347              : }
     348              : 
     349              : //---------------------------------------------------------
     350              : //   drop
     351              : //---------------------------------------------------------
     352              : 
     353            0 : EngravingItem* Box::drop(Transaction& tx, EditData& data)
     354              : {
     355            0 :     EngravingItem* e = data.dropElement;
     356            0 :     if (e->flag(ElementFlag::ON_STAFF)) {
     357            0 :         return 0;
     358              :     }
     359            0 :     if (MScore::debugMode) {
     360            0 :         LOGD("<%s>", e->typeName());
     361              :     }
     362            0 :     switch (e->type()) {
     363            0 :     case ElementType::STAFF_TEXT:
     364              :     {
     365            0 :         Text* text = Factory::createText(this, TextStyleType::FRAME);
     366            0 :         text->setXmlText(toStaffText(e)->xmlText());
     367            0 :         score()->undoAddElement(text);
     368            0 :         delete e;
     369            0 :         return text;
     370              :     }
     371              : 
     372            0 :     case ElementType::ACTION_ICON:
     373            0 :         switch (toActionIcon(e)->actionType()) {
     374            0 :         case ActionIconType::VFRAME:
     375            0 :             score()->insertBox(ElementType::VBOX, this);
     376            0 :             break;
     377            0 :         case ActionIconType::TFRAME:
     378            0 :             score()->insertBox(ElementType::TBOX, this);
     379            0 :             break;
     380            0 :         case ActionIconType::FFRAME:
     381            0 :             score()->insertBox(ElementType::FBOX, this);
     382            0 :             break;
     383            0 :         case ActionIconType::HFRAME:
     384            0 :             score()->insertBox(ElementType::HBOX, this);
     385            0 :             break;
     386            0 :         case ActionIconType::MEASURE:
     387            0 :             score()->insertMeasure(ElementType::MEASURE, this);
     388            0 :             break;
     389            0 :         default:
     390            0 :             break;
     391              :         }
     392            0 :         break;
     393              : 
     394            0 :     case ElementType::TEXT:
     395              :     case ElementType::IMAGE:
     396              :     case ElementType::SYMBOL:
     397            0 :         e->setOwnershipParent(this);
     398            0 :         score()->undoAddElement(e);
     399            0 :         return e;
     400            0 :     default:
     401            0 :         break;
     402              :     }
     403              : 
     404            0 :     return MeasureBase::drop(tx, data);
     405              : }
     406              : 
     407           51 : void Box::manageExclusionFromParts(bool exclude)
     408              : {
     409              :     // manage Layout Breaks - remove old ones first
     410           51 :     LayoutBreak* sectionBreak = sectionBreakElement();
     411           51 :     if (sectionBreak) {
     412            0 :         toEngravingItem(sectionBreak)->manageExclusionFromParts(true);
     413              :     }
     414              : 
     415           51 :     bool titleFrame = isTitleFrame();
     416           51 :     if (exclude) {
     417            2 :         const std::list<EngravingObject*> links = linkList();
     418            6 :         for (EngravingObject* linkedObject : links) {
     419              :             // Only remove title frame from score
     420            4 :             if (linkedObject->score() == score() || (!this->score()->isMaster() && titleFrame && !linkedObject->score()->isMaster())) {
     421            2 :                 continue;
     422              :             }
     423            2 :             EngravingItem* linkedItem = toEngravingItem(linkedObject);
     424            2 :             if (linkedItem->selected()) {
     425            0 :                 linkedItem->score()->deselect(linkedItem);
     426              :             }
     427            2 :             linkedItem->score()->undoRemoveElement(linkedItem, false);
     428            2 :             linkedItem->undoUnlink();
     429              :         }
     430              : 
     431              :         // manage Layout Breaks - there are no linked boxes, so add linked Line Breaks to previous measure
     432            2 :         if (sectionBreak && !titleFrame) {
     433            0 :             if (MeasureBase* prevMeasure = this->prevMeasure()) {
     434            0 :                 for (Score* score : masterScore()->scoreList()) {
     435            0 :                     if (score == this->score()) {
     436            0 :                         continue;
     437              :                     }
     438            0 :                     if (MeasureBase* localPrevMeasure = score->tick2measure(prevMeasure->tick())) {
     439            0 :                         EngravingItem* newSectionBreak = sectionBreak->linkedClone();
     440            0 :                         newSectionBreak->setScore(score);
     441            0 :                         newSectionBreak->setOwnershipParent(localPrevMeasure);
     442            0 :                         score->doUndoAddElement(newSectionBreak);
     443              :                     }
     444            0 :                 }
     445              :             }
     446              :         }
     447            2 :     } else {
     448          101 :         for (Score* score : masterScore()->scoreList()) {
     449           52 :             if (score == this->score() || (titleFrame && !this->score()->isMaster() && !score->isMaster())) {
     450           33 :                 continue;
     451              :             }
     452              : 
     453           19 :             MeasureBase* newMB = next() ? next()->getInScore(score, true) : nullptr;
     454           19 :             Score::InsertMeasureOptions options;
     455           19 :             options.cloneBoxToAllParts = false;
     456           19 :             MeasureBase* newFrame = score->insertBox(type(), newMB, options);
     457           19 :             newFrame->setExcludeFromOtherParts(false);
     458              :             // newFrame->setSizeIsSpatiumDependent(!titleFrame);
     459              : 
     460           19 :             if (!isFBox()) {
     461           19 :                 for (EngravingItem* item : el()) {
     462              :                     // Don't add instrument name from current part
     463            0 :                     if (item->isText() && toText(item)->textStyleType() == TextStyleType::INSTRUMENT_EXCERPT) {
     464            0 :                         continue;
     465              :                     }
     466              :                     // add frame items (Layout Break, Title, ...)
     467            0 :                     newFrame->add(item->linkedClone());
     468              :                 }
     469              :             }
     470              : 
     471           19 :             if (isTBox()) {
     472            0 :                 Text* thisText = toTBox(this)->text();
     473            0 :                 toTBox(newFrame)->add(thisText->linkedClone());
     474              :             }
     475              : 
     476           19 :             if (!score->isMaster() && titleFrame) {
     477              :                 // Title frame - add part name
     478            1 :                 String partLabel = score->name();
     479            1 :                 if (!partLabel.empty()) {
     480            1 :                     Text* txt = Factory::createText(newFrame, TextStyleType::INSTRUMENT_EXCERPT);
     481            1 :                     txt->setPlainText(partLabel);
     482            1 :                     newFrame->add(txt);
     483              : 
     484            1 :                     score->setMetaTag(u"partName", partLabel);
     485              :                 }
     486            1 :             }
     487              : 
     488           19 :             newFrame->linkTo(this);
     489           49 :         }
     490              :     }
     491           51 : }
     492              : 
     493              : //---------------------------------------------------------
     494              : //   drag
     495              : //---------------------------------------------------------
     496              : 
     497            0 : RectF HBox::drag(EditData& data)
     498              : {
     499            0 :     if (!isMovable()) {
     500            0 :         return RectF();
     501              :     }
     502            0 :     RectF r(canvasBoundingRect());
     503            0 :     double diff = data.evtDelta.x();
     504            0 :     double x1   = offset().x() + diff;
     505            0 :     EngravingItem* parent = layoutParent();
     506            0 :     if (parent && parent->isVBox()) {
     507            0 :         VBox* vb = toVBox(parent);
     508            0 :         double x2 = parent->width() - width() - (vb->leftMargin() + vb->rightMargin()) * DPMM;
     509            0 :         if (x1 < 0.0) {
     510            0 :             x1 = 0.0;
     511            0 :         } else if (x1 > x2) {
     512            0 :             x1 = x2;
     513              :         }
     514              :     }
     515            0 :     setOffset(PointF(x1, 0.0));
     516              : //      setStartDragPosition(data.delta);
     517            0 :     return canvasBoundingRect().united(r);
     518              : }
     519              : 
     520              : //---------------------------------------------------------
     521              : //   isMovable
     522              : //---------------------------------------------------------
     523              : 
     524            0 : bool HBox::isMovable() const
     525              : {
     526            0 :     return ownershipParent() && (ownershipParent()->isHBox() || ownershipParent()->isVBox());
     527              : }
     528              : 
     529              : //---------------------------------------------------------
     530              : //   getProperty
     531              : //---------------------------------------------------------
     532              : 
     533          612 : PropertyValue HBox::getProperty(Pid propertyId) const
     534              : {
     535          612 :     switch (propertyId) {
     536           30 :     case Pid::CREATE_SYSTEM_HEADER:
     537           30 :         return createSystemHeader();
     538          582 :     default:
     539          582 :         return Box::getProperty(propertyId);
     540              :     }
     541              : }
     542              : 
     543              : //---------------------------------------------------------
     544              : //   setProperty
     545              : //---------------------------------------------------------
     546              : 
     547           89 : bool HBox::setProperty(Pid propertyId, const PropertyValue& v)
     548              : {
     549           89 :     switch (propertyId) {
     550            0 :     case Pid::CREATE_SYSTEM_HEADER:
     551            0 :         setCreateSystemHeader(v.toBool());
     552            0 :         triggerLayout();
     553            0 :         break;
     554           89 :     default:
     555           89 :         return Box::setProperty(propertyId, v);
     556              :     }
     557            0 :     return true;
     558              : }
     559              : 
     560              : //---------------------------------------------------------
     561              : //   propertyDefault
     562              : //---------------------------------------------------------
     563              : 
     564          671 : PropertyValue HBox::propertyDefault(Pid id) const
     565              : {
     566          671 :     switch (id) {
     567           30 :     case Pid::CREATE_SYSTEM_HEADER:
     568           30 :         return true;
     569           87 :     case Pid::BOX_WIDTH:
     570           87 :         return 5.0_sp;
     571          554 :     default:
     572          554 :         return Box::propertyDefault(id);
     573              :     }
     574              : }
     575              : 
     576              : //---------------------------------------------------------
     577              : //   VBox
     578              : //---------------------------------------------------------
     579              : 
     580         1336 : VBox::VBox(const ElementType& type, Score* parent)
     581         1336 :     : Box(type, parent)
     582              : {
     583         1336 :     initElementStyle(&boxStyle);
     584              : 
     585         1336 :     resetProperty(Pid::BOX_HEIGHT);
     586              : 
     587         1336 :     setLineBreak(true);
     588         1336 : }
     589              : 
     590         1229 : VBox::VBox(Score* parent)
     591         1229 :     : VBox(ElementType::VBOX, parent)
     592              : {
     593         1229 : }
     594              : 
     595         2348 : void VBox::add(EngravingItem* e)
     596              : {
     597         2348 :     IF_ASSERT_FAILED(e) {
     598            0 :         return;
     599              :     }
     600         2348 :     if (e->isLayoutBreak()) {
     601           11 :         const LayoutBreakType type = toLayoutBreak(e)->layoutBreakType();
     602           11 :         if (type == LayoutBreakType::LINE) {
     603            0 :             LOGW("Cannot add %s", e->typeName());
     604            0 :             return;
     605              :         }
     606              :     }
     607         2348 :     if (e->isActionIcon()) {
     608            0 :         const ActionIconType type = toActionIcon(e)->actionType();
     609            0 :         if (type == ActionIconType::SYSTEM_LOCK) {
     610            0 :             LOGW("Cannot add %s", e->typeName());
     611            0 :             return;
     612              :         }
     613              :     }
     614         2348 :     Box::add(e);
     615              : }
     616              : 
     617         2296 : double VBox::minHeight() const
     618              : {
     619         2296 :     return absoluteFromSpatium(10_sp);
     620              : }
     621              : 
     622            0 : double VBox::maxHeight() const
     623              : {
     624            0 :     return absoluteFromSpatium(30_sp);
     625              : }
     626              : 
     627        12791 : PropertyValue VBox::getProperty(Pid propertyId) const
     628              : {
     629        12791 :     switch (propertyId) {
     630         2768 :     case Pid::BOX_AUTOSIZE:
     631         2768 :         return isAutoSizeEnabled();
     632            1 :     case Pid::PADDING_TO_NOTATION_ABOVE:
     633            1 :         return m_paddingToNotationAbove;
     634            1 :     case Pid::PADDING_TO_NOTATION_BELOW:
     635            1 :         return m_paddingToNotationBelow;
     636        10021 :     default:
     637        10021 :         return Box::getProperty(propertyId);
     638              :     }
     639              : }
     640              : 
     641              : //---------------------------------------------------------
     642              : //   propertyDefault
     643              : //---------------------------------------------------------
     644              : 
     645        11628 : PropertyValue VBox::propertyDefault(Pid id) const
     646              : {
     647        11628 :     switch (id) {
     648         1336 :     case Pid::BOX_HEIGHT:
     649         1336 :         return 10.0_sp;
     650        10292 :     default:
     651        10292 :         return Box::propertyDefault(id);
     652              :     }
     653              : }
     654              : 
     655         7263 : bool VBox::setProperty(Pid propertyId, const PropertyValue& v)
     656              : {
     657         7263 :     switch (propertyId) {
     658         1406 :     case Pid::PADDING_TO_NOTATION_ABOVE:
     659         1406 :         m_paddingToNotationAbove = v.value<Spatium>();
     660         1406 :         break;
     661         1406 :     case Pid::PADDING_TO_NOTATION_BELOW:
     662         1406 :         m_paddingToNotationBelow = v.value<Spatium>();
     663         1406 :         break;
     664         4451 :     default:
     665         4451 :         return Box::setProperty(propertyId, v);
     666              :     }
     667              : 
     668         2812 :     triggerLayout();
     669         2812 :     return true;
     670              : }
     671              : 
     672              : //---------------------------------------------------------
     673              : //   startDragGrip
     674              : //---------------------------------------------------------
     675              : 
     676            0 : void VBox::startDragGrip(EditData& ed)
     677              : {
     678            0 :     const double sp = sizeIsSpatiumDependent() ? spatium() : style().defaultSpatium();
     679            0 :     if (isAutoSizeEnabled()) {
     680            0 :         setAutoSizeEnabled(false);
     681            0 :         setBoxHeight(Spatium(height() / sp));
     682              :     }
     683            0 :     Box::startDragGrip(ed);
     684            0 : }
     685              : 
     686            0 : bool VBox::acceptDrop(EditData& data) const
     687              : {
     688            0 :     const EngravingItem* e = data.dropElement;
     689            0 :     IF_ASSERT_FAILED(e) {
     690            0 :         return false;
     691              :     }
     692            0 :     if (e->isLayoutBreak()) {
     693            0 :         const LayoutBreakType type = toLayoutBreak(e)->layoutBreakType();
     694            0 :         if (type == LayoutBreakType::LINE) {
     695            0 :             return false;
     696              :         }
     697              :     }
     698            0 :     if (e->isActionIcon()) {
     699            0 :         const ActionIconType type = toActionIcon(e)->actionType();
     700            0 :         if (type == ActionIconType::SYSTEM_LOCK) {
     701            0 :             return false;
     702              :         }
     703              :     }
     704            0 :     return Box::acceptDrop(data);
     705              : }
     706              : 
     707              : //---------------------------------------------------------
     708              : //   add
     709              : ///   Add new EngravingItem \a e to fret diagram box
     710              : //---------------------------------------------------------
     711              : 
     712            5 : FBox::FBox(Score* parent)
     713            5 :     : VBox(ElementType::FBOX, parent)
     714              : {
     715            5 :     resetProperty(Pid::FRET_FRAME_TEXT_SCALE);
     716            5 :     resetProperty(Pid::FRET_FRAME_DIAGRAM_SCALE);
     717            5 :     resetProperty(Pid::FRET_FRAME_COLUMN_GAP);
     718            5 :     resetProperty(Pid::FRET_FRAME_ROW_GAP);
     719            5 :     resetProperty(Pid::FRET_FRAME_CHORDS_PER_ROW);
     720            5 :     resetProperty(Pid::FRET_FRAME_H_ALIGN);
     721              : 
     722            5 :     resetProperty(Pid::LEFT_MARGIN);
     723            5 :     resetProperty(Pid::RIGHT_MARGIN);
     724            5 :     resetProperty(Pid::TOP_MARGIN);
     725            5 :     resetProperty(Pid::BOTTOM_MARGIN);
     726            5 :     resetProperty(Pid::TOP_GAP);
     727            5 :     resetProperty(Pid::BOTTOM_GAP);
     728            5 :     resetProperty(Pid::EXCLUDE_FROM_OTHER_PARTS);
     729            5 :     resetProperty(Pid::APPEARANCE_LINKED_TO_MASTER);
     730            5 :     resetProperty(Pid::POSITION_LINKED_TO_MASTER);
     731            5 : }
     732              : 
     733            7 : void FBox::init()
     734              : {
     735            7 :     StringList oldDiagramsNames;
     736            7 :     std::vector<FretDiagram*> oldDiagrams;
     737           19 :     for (EngravingItem* element : el()) {
     738           12 :         FretDiagram* diagram = toFretDiagram(element);
     739           12 :         oldDiagrams.push_back(diagram);
     740           12 :         oldDiagramsNames.push_back(diagram->harmonyDisplayText());
     741              :     }
     742              : 
     743            7 :     StringList diagramsNamesInScore;
     744            7 :     std::vector<EngravingItem*> harmonyOrDiagramsInScore;
     745          105 :     for (mu::engraving::Segment* segment = score()->firstSegment(mu::engraving::SegmentType::ChordRest); segment;
     746           98 :          segment = segment->next1(mu::engraving::SegmentType::ChordRest)) {
     747          133 :         for (EngravingItem* item : segment->annotations()) {
     748           35 :             if (!item || !item->part()) {
     749            1 :                 continue;
     750              :             }
     751              : 
     752           35 :             if (item->isHarmony() && toHarmony(item)->harmonyType() != HarmonyType::STANDARD) {
     753            0 :                 continue;
     754              :             }
     755              : 
     756           35 :             if (!(item->isHarmony() || item->isFretDiagram())) {
     757            0 :                 continue;
     758              :             }
     759              : 
     760           35 :             String harmonyName = item->isHarmony() ? toHarmony(item)->displayText()
     761           35 :                                  : item->isFretDiagram() ? toFretDiagram(item)->harmonyDisplayText()
     762           35 :                                  : String();
     763           35 :             if (harmonyName.empty() || muse::contains(diagramsNamesInScore, harmonyName)) {
     764            1 :                 continue;
     765              :             }
     766              : 
     767           34 :             harmonyOrDiagramsInScore.push_back(item);
     768           34 :             diagramsNamesInScore.push_back(harmonyName);
     769           35 :         }
     770              :     }
     771              : 
     772            7 :     m_diagramsOrderInScore = diagramsNamesInScore;
     773              : 
     774           19 :     for (size_t i = 0; i < oldDiagramsNames.size(); ++i) {
     775           12 :         String oldName = oldDiagramsNames[i];
     776           12 :         if (!muse::contains(diagramsNamesInScore, oldName)) {
     777            0 :             score()->undo(new RemoveFretDiagramFromFretBox(oldDiagrams[i]));
     778              :         }
     779           12 :     }
     780              : 
     781           41 :     for (size_t i = 0; i < diagramsNamesInScore.size(); ++i) {
     782           34 :         String newName = diagramsNamesInScore[i];
     783           34 :         if (!muse::contains(oldDiagramsNames, newName)) {
     784           22 :             FretDiagram* newDiagram = FretDiagram::makeFromHarmonyOrFretDiagram(harmonyOrDiagramsInScore[i]);
     785           22 :             newDiagram->setOwnershipParent(this);
     786           22 :             String nameOfDiagramBeforeThis = i > 0 ? diagramsNamesInScore[i - 1] : String();
     787           22 :             size_t idx = computeInsertionIdx(nameOfDiagramBeforeThis);
     788           22 :             score()->undo(new AddFretDiagramToFretBox(newDiagram, idx));
     789           22 :         }
     790           34 :     }
     791            7 : }
     792              : 
     793            0 : void FBox::add(EngravingItem* e)
     794              : {
     795            0 :     e->setOwnershipParent(this);
     796            0 :     if (e->isFretDiagram()) {
     797            0 :         addAtIdx(toFretDiagram(e), muse::nidx);
     798              :     } else {
     799            0 :         LOGD("FBox::add: element not allowed");
     800            0 :         return;
     801              :     }
     802              : }
     803              : 
     804           22 : void FBox::addAtIdx(FretDiagram* fretDiagram, size_t idx)
     805              : {
     806           22 :     fretDiagram->setTrack(muse::nidx);
     807           22 :     fretDiagram->setFlag(ElementFlag::MOVABLE, false);
     808           22 :     fretDiagram->setFlag(ElementFlag::ON_STAFF, false);
     809              : 
     810           22 :     Harmony* harmony = fretDiagram->harmony();
     811           22 :     harmony->setTrack(muse::nidx);
     812           22 :     harmony->setFlag(ElementFlag::MOVABLE, false);
     813           22 :     harmony->setFlag(ElementFlag::ON_STAFF, false);
     814              : 
     815           22 :     if (idx < m_el.size()) {
     816            0 :         m_el.insert(m_el.begin() + idx, fretDiagram);
     817              :     } else {
     818           22 :         m_el.push_back(fretDiagram);
     819              :     }
     820              : 
     821           22 :     fretDiagram->added();
     822           22 : }
     823              : 
     824           22 : size_t FBox::computeInsertionIdx(const String& nameOfDiagramBeforeThis)
     825              : {
     826           22 :     if (nameOfDiagramBeforeThis.empty()) {
     827            5 :         return 0;
     828              :     }
     829              : 
     830           60 :     for (size_t i = 0; i < m_el.size(); ++i) {
     831           43 :         FretDiagram* fretDiagram = toFretDiagram(m_el[i]);
     832           43 :         if (fretDiagram->harmonyDisplayText() == nameOfDiagramBeforeThis) {
     833            0 :             return i + 1;
     834              :         }
     835              :     }
     836              : 
     837           17 :     return muse::nidx;
     838              : }
     839              : 
     840           53 : PropertyValue FBox::getProperty(Pid propertyId) const
     841              : {
     842           53 :     switch (propertyId) {
     843            0 :     case Pid::FRET_FRAME_TEXT_SCALE:
     844            0 :         return m_textScale;
     845            0 :     case Pid::FRET_FRAME_DIAGRAM_SCALE:
     846            0 :         return m_diagramScale;
     847            0 :     case Pid::FRET_FRAME_COLUMN_GAP:
     848            0 :         return m_columnGap;
     849            0 :     case Pid::FRET_FRAME_ROW_GAP:
     850            0 :         return m_rowGap;
     851            0 :     case Pid::FRET_FRAME_CHORDS_PER_ROW:
     852            0 :         return m_chordsPerRow;
     853            0 :     case Pid::FRET_FRAME_H_ALIGN:
     854            0 :         return static_cast<int>(m_contentAlignmentH);
     855           12 :     case Pid::LEFT_MARGIN:
     856           12 :         return m_contentAlignmentH == AlignH::LEFT ? VBox::getProperty(propertyId) : PropertyValue();
     857           12 :     case Pid::RIGHT_MARGIN:
     858           12 :         return m_contentAlignmentH == AlignH::RIGHT ? VBox::getProperty(propertyId) : PropertyValue();
     859            4 :     case Pid::FRET_FRAME_DIAGRAMS_ORDER:
     860            4 :         return diagramsOrder().join(FRET_BOX_DIAGRAMS_SEPARATOR);
     861           25 :     default:
     862           25 :         return VBox::getProperty(propertyId);
     863              :     }
     864              : }
     865              : 
     866           87 : bool FBox::setProperty(Pid propertyId, const PropertyValue& val)
     867              : {
     868           87 :     switch (propertyId) {
     869            5 :     case Pid::FRET_FRAME_TEXT_SCALE:
     870            5 :         m_textScale = val.toDouble();
     871            5 :         break;
     872            5 :     case Pid::FRET_FRAME_DIAGRAM_SCALE:
     873            5 :         m_diagramScale = val.toDouble();
     874            5 :         break;
     875            5 :     case Pid::FRET_FRAME_COLUMN_GAP:
     876            5 :         m_columnGap = val.value<Spatium>();
     877            5 :         break;
     878            5 :     case Pid::FRET_FRAME_ROW_GAP:
     879            5 :         m_rowGap = val.value<Spatium>();
     880            5 :         break;
     881            5 :     case Pid::FRET_FRAME_CHORDS_PER_ROW:
     882            5 :         m_chordsPerRow = val.toInt();
     883            5 :         break;
     884            5 :     case Pid::FRET_FRAME_H_ALIGN:
     885            5 :         m_contentAlignmentH = static_cast<AlignH>(val.toInt());
     886            5 :         resetProperty(Pid::LEFT_MARGIN);
     887            5 :         resetProperty(Pid::RIGHT_MARGIN);
     888            5 :         break;
     889            2 :     case Pid::FRET_FRAME_DIAGRAMS_ORDER:
     890            2 :         reorderElements(val.value<String>().split(FRET_BOX_DIAGRAMS_SEPARATOR));
     891            2 :         break;
     892           55 :     default:
     893           55 :         return VBox::setProperty(propertyId, val);
     894              :     }
     895              : 
     896           32 :     triggerLayout();
     897           32 :     return true;
     898              : }
     899              : 
     900           85 : PropertyValue FBox::propertyDefault(Pid propertyId) const
     901              : {
     902           85 :     switch (propertyId) {
     903           10 :     case Pid::FRET_FRAME_TEXT_SCALE:
     904              :     case Pid::FRET_FRAME_DIAGRAM_SCALE:
     905           10 :         return 1.0;
     906           10 :     case Pid::FRET_FRAME_COLUMN_GAP:
     907              :     case Pid::FRET_FRAME_ROW_GAP:
     908           10 :         return 3.0_sp;
     909            5 :     case Pid::FRET_FRAME_CHORDS_PER_ROW:
     910            5 :         return 8;
     911            5 :     case Pid::FRET_FRAME_H_ALIGN:
     912            5 :         return static_cast<int>(AlignH::HCENTER);
     913            0 :     case Pid::FRET_FRAME_DIAGRAMS_ORDER:
     914            0 :         return m_diagramsOrderInScore.join(FRET_BOX_DIAGRAMS_SEPARATOR);
     915            5 :     case Pid::EXCLUDE_FROM_OTHER_PARTS:
     916            5 :         return true;
     917           10 :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     918              :     case Pid::POSITION_LINKED_TO_MASTER:
     919           10 :         return false;
     920           40 :     default:
     921           40 :         return VBox::propertyDefault(propertyId);
     922              :     }
     923              : }
     924              : 
     925            0 : int FBox::gripsCount() const
     926              : {
     927            0 :     return 0;
     928              : }
     929              : 
     930            0 : Grip FBox::initialEditModeGrip() const
     931              : {
     932            0 :     return Grip::NO_GRIP;
     933              : }
     934              : 
     935            0 : Grip FBox::defaultGrip() const
     936              : {
     937            0 :     return Grip::NO_GRIP;
     938              : }
     939              : 
     940            0 : std::vector<PointF> FBox::gripsPositions(const EditData&) const
     941              : {
     942            0 :     return {};
     943              : }
     944              : 
     945            1 : void FBox::undoReorderElements(const StringList& newOrder)
     946              : {
     947            1 :     undoChangeProperty(Pid::FRET_FRAME_DIAGRAMS_ORDER, newOrder.join(FRET_BOX_DIAGRAMS_SEPARATOR));
     948            1 :     triggerLayout();
     949            1 : }
     950              : 
     951            2 : void FBox::reorderElements(const StringList& newOrder)
     952              : {
     953            2 :     std::sort(m_el.begin(), m_el.end(), [&](EngravingItem* a, EngravingItem* b) {
     954           14 :         String nameA = toFretDiagram(a)->harmonyDisplayText();
     955           14 :         String nameB = toFretDiagram(b)->harmonyDisplayText();
     956           14 :         auto iterA = std::find(newOrder.begin(), newOrder.end(), nameA);
     957           14 :         auto iterB = std::find(newOrder.begin(), newOrder.end(), nameB);
     958           28 :         return iterA < iterB;
     959           14 :     });
     960            2 : }
     961              : 
     962            4 : StringList FBox::diagramsOrder() const
     963              : {
     964            4 :     StringList result;
     965           20 :     for (EngravingItem* item : m_el) {
     966           16 :         result.push_back(toFretDiagram(item)->harmonyDisplayText());
     967              :     }
     968              : 
     969            4 :     return result;
     970            0 : }
     971              : 
     972              : //---------------------------------------------------------
     973              : //   TBox
     974              : //---------------------------------------------------------
     975              : 
     976          102 : TBox::TBox(Score* parent)
     977          102 :     : VBox(ElementType::TBOX, parent)
     978              : {
     979          102 :     resetProperty(Pid::BOX_HEIGHT);
     980          102 :     m_text  = Factory::createText(this, TextStyleType::FRAME);
     981          102 :     m_text->setLayoutToParentWidth(true);
     982          102 : }
     983              : 
     984            0 : TBox::TBox(const TBox& tbox)
     985            0 :     : VBox(tbox)
     986              : {
     987            0 :     if (tbox.m_text) {
     988            0 :         add(Factory::copyText(*(tbox.m_text)));
     989              :     }
     990            0 : }
     991              : 
     992          202 : TBox::~TBox()
     993              : {
     994          101 :     delete m_text;
     995          202 : }
     996              : 
     997            0 : void TBox::scanElements(std::function<void(EngravingItem*)> func)
     998              : {
     999            0 :     m_text->scanElements(func);
    1000            0 :     Box::scanElements(func);
    1001            0 : }
    1002              : 
    1003              : //---------------------------------------------------------
    1004              : //   drop
    1005              : //---------------------------------------------------------
    1006              : 
    1007            0 : EngravingItem* TBox::drop(Transaction& tx, EditData& data)
    1008              : {
    1009            0 :     EngravingItem* e = data.dropElement;
    1010            0 :     switch (e->type()) {
    1011            0 :     case ElementType::TEXT:
    1012            0 :         m_text->undoChangeProperty(Pid::TEXT, toText(e)->xmlText());
    1013            0 :         delete e;
    1014            0 :         return m_text;
    1015            0 :     default:
    1016            0 :         return VBox::drop(tx, data);
    1017              :     }
    1018              : }
    1019              : 
    1020              : //---------------------------------------------------------
    1021              : //   add
    1022              : ///   Add new EngravingItem \a el to TBox
    1023              : //---------------------------------------------------------
    1024              : 
    1025            9 : void TBox::add(EngravingItem* e)
    1026              : {
    1027            9 :     if (e->isText()) {
    1028            0 :         if (m_text) {
    1029            0 :             delete m_text;
    1030              :         }
    1031            0 :         m_text = toText(e);
    1032            0 :         m_text->setLayoutToParentWidth(true);
    1033            0 :         e->setOwnershipParent(this);
    1034            0 :         e->added();
    1035              :     } else {
    1036            9 :         VBox::add(e);
    1037              :     }
    1038            9 : }
    1039              : 
    1040              : //---------------------------------------------------------
    1041              : //   remove
    1042              : //---------------------------------------------------------
    1043              : 
    1044            0 : void TBox::remove(EngravingItem* el)
    1045              : {
    1046            0 :     if (el == m_text) {
    1047              :         // does not normally happen, since Score::deleteItem() handles this directly
    1048              :         // but if it does:
    1049              :         // replace with new empty text element
    1050              :         // this keeps undo/redo happier than just clearing the text
    1051            0 :         LOGD("TBox::remove() - replacing _text");
    1052            0 :         m_text = Factory::createText(this, TextStyleType::FRAME);
    1053            0 :         m_text->setLayoutToParentWidth(true);
    1054            0 :         el->removed();
    1055              :     } else {
    1056            0 :         VBox::remove(el);
    1057              :     }
    1058            0 : }
    1059              : 
    1060              : //---------------------------------------------------------
    1061              : //   propertyDefault
    1062              : //---------------------------------------------------------
    1063              : 
    1064          125 : PropertyValue TBox::propertyDefault(Pid id) const
    1065              : {
    1066          125 :     switch (id) {
    1067          103 :     case Pid::BOX_HEIGHT:
    1068          103 :         return 1_sp;
    1069           22 :     default:
    1070           22 :         return VBox::propertyDefault(id);
    1071              :     }
    1072              : }
    1073              : 
    1074              : //---------------------------------------------------------
    1075              : //   accessibleExtraInfo
    1076              : //---------------------------------------------------------
    1077              : 
    1078            0 : String Box::accessibleExtraInfo() const
    1079              : {
    1080            0 :     String rez;
    1081            0 :     for (EngravingItem* e : el()) {
    1082            0 :         rez += u' ' + e->screenReaderInfo();
    1083              :     }
    1084            0 :     return rez;
    1085            0 : }
    1086              : 
    1087              : //---------------------------------------------------------
    1088              : //   accessibleExtraInfo
    1089              : //---------------------------------------------------------
    1090              : 
    1091            0 : String TBox::accessibleExtraInfo() const
    1092              : {
    1093            0 :     String rez = m_text->screenReaderInfo();
    1094            0 :     return rez;
    1095              : }
    1096              : 
    1097            0 : int TBox::gripsCount() const
    1098              : {
    1099            0 :     return 0;
    1100              : }
    1101              : 
    1102            0 : Grip TBox::initialEditModeGrip() const
    1103              : {
    1104            0 :     return Grip::NO_GRIP;
    1105              : }
    1106              : 
    1107            0 : Grip TBox::defaultGrip() const
    1108              : {
    1109            0 :     return Grip::NO_GRIP;
    1110              : }
    1111              : }
        

Generated by: LCOV version 2.0-1