LCOV - code coverage report
Current view: top level - src/engraving/rendering/single - singlelayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 1648 0
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) 2023 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 "singlelayout.h"
      24              : 
      25              : #include "draw/fontmetrics.h"
      26              : 
      27              : #include "types/types.h"
      28              : #include "types/typesconv.h"
      29              : #include "types/symnames.h"
      30              : 
      31              : #include "dom/dummyparent.h"
      32              : 
      33              : #include "dom/factory.h"
      34              : 
      35              : #include "dom/engravingitem.h"
      36              : #include "dom/score.h"
      37              : 
      38              : #include "dom/accidental.h"
      39              : #include "dom/actionicon.h"
      40              : #include "dom/ambitus.h"
      41              : #include "dom/articulation.h"
      42              : #include "dom/bagpembell.h"
      43              : #include "dom/barline.h"
      44              : #include "dom/bend.h"
      45              : #include "dom/bracket.h"
      46              : #include "dom/breath.h"
      47              : #include "dom/capo.h"
      48              : #include "dom/chordline.h"
      49              : #include "dom/clef.h"
      50              : #include "dom/dynamic.h"
      51              : #include "dom/expression.h"
      52              : #include "dom/fermata.h"
      53              : #include "dom/fingering.h"
      54              : #include "dom/fret.h"
      55              : #include "dom/glissando.h"
      56              : #include "dom/gradualtempochange.h"
      57              : #include "dom/guitarbend.h"
      58              : #include "dom/hairpin.h"
      59              : #include "dom/hammeronpulloff.h"
      60              : #include "dom/harppedaldiagram.h"
      61              : #include "dom/instrchange.h"
      62              : #include "dom/jump.h"
      63              : #include "dom/keysig.h"
      64              : #include "dom/letring.h"
      65              : #include "dom/line.h"
      66              : #include "dom/lyrics.h"
      67              : #include "dom/marker.h"
      68              : #include "dom/measurenumber.h"
      69              : #include "dom/measurerepeat.h"
      70              : #include "dom/note.h"
      71              : #include "dom/noteline.h"
      72              : #include "dom/ornament.h"
      73              : #include "dom/ottava.h"
      74              : #include "dom/page.h"
      75              : #include "dom/palmmute.h"
      76              : #include "dom/pedal.h"
      77              : #include "dom/playcounttext.h"
      78              : #include "dom/playtechannotation.h"
      79              : #include "dom/rehearsalmark.h"
      80              : #include "dom/slur.h"
      81              : #include "dom/soundflag.h"
      82              : #include "dom/spacer.h"
      83              : #include "dom/staff.h"
      84              : #include "dom/stafftext.h"
      85              : #include "dom/stafftypechange.h"
      86              : #include "dom/sticking.h"
      87              : #include "dom/stringtunings.h"
      88              : #include "dom/symbol.h"
      89              : #include "dom/systemtext.h"
      90              : #include "dom/tapping.h"
      91              : #include "dom/tempotext.h"
      92              : #include "dom/text.h"
      93              : #include "dom/textline.h"
      94              : #include "dom/textlinebase.h"
      95              : #include "dom/timesig.h"
      96              : #include "dom/tremolobar.h"
      97              : #include "dom/tremolosinglechord.h"
      98              : #include "dom/tremolotwochord.h"
      99              : #include "dom/trill.h"
     100              : #include "dom/vibrato.h"
     101              : #include "dom/volta.h"
     102              : #include "dom/whammybar.h"
     103              : 
     104              : #include "dom/utils.h"
     105              : 
     106              : #include "rendering/score/tlayout.h"
     107              : #include "rendering/score/textlayout.h"
     108              : #include "rendering/score/tremololayout.h"
     109              : #include "rendering/score/chordlayout.h"
     110              : #include "rendering/score/slurtielayout.h"
     111              : 
     112              : #include "log.h"
     113              : 
     114              : using namespace muse;
     115              : using namespace muse::draw;
     116              : using namespace mu::engraving;
     117              : using namespace mu::engraving::rendering::score;
     118              : using namespace mu::engraving::rendering::single;
     119              : 
     120            0 : void SingleLayout::layoutItem(EngravingItem* item)
     121              : {
     122            0 :     Context ctx(item->score());
     123              : 
     124            0 :     switch (item->type()) {
     125            0 :     case ElementType::ACCIDENTAL:   layout(toAccidental(item), ctx);
     126            0 :         break;
     127            0 :     case ElementType::ACTION_ICON:  layout(toActionIcon(item), ctx);
     128            0 :         break;
     129            0 :     case ElementType::AMBITUS:      layout(toAmbitus(item), ctx);
     130            0 :         break;
     131            0 :     case ElementType::ARPEGGIO:     layout(toArpeggio(item), ctx);
     132            0 :         break;
     133            0 :     case ElementType::CHORD_BRACKET: layout(toChordBracket(item), ctx);
     134            0 :         break;
     135            0 :     case ElementType::ARTICULATION: layout(toArticulation(item), ctx);
     136            0 :         break;
     137            0 :     case ElementType::BAGPIPE_EMBELLISHMENT: layout(toBagpipeEmbellishment(item), ctx);
     138            0 :         break;
     139            0 :     case ElementType::BAR_LINE:     layout(toBarLine(item), ctx);
     140            0 :         break;
     141            0 :     case ElementType::BEND:         layout(toBend(item), ctx);
     142            0 :         break;
     143            0 :     case ElementType::BRACKET:      layout(toBracket(item), ctx);
     144            0 :         break;
     145            0 :     case ElementType::BREATH:       layout(toBreath(item), ctx);
     146            0 :         break;
     147            0 :     case ElementType::CAPO:         layout(toCapo(item), ctx);
     148            0 :         break;
     149            0 :     case ElementType::CHORDLINE:    layout(toChordLine(item), ctx);
     150            0 :         break;
     151            0 :     case ElementType::CLEF:         layout(toClef(item), ctx);
     152            0 :         break;
     153            0 :     case ElementType::DYNAMIC:      layout(toDynamic(item), ctx);
     154            0 :         break;
     155            0 :     case ElementType::EXPRESSION:   layout(toExpression(item), ctx);
     156            0 :         break;
     157            0 :     case ElementType::FERMATA:      layout(toFermata(item), ctx);
     158            0 :         break;
     159            0 :     case ElementType::FINGERING:    layout(toFingering(item), ctx);
     160            0 :         break;
     161            0 :     case ElementType::FRET_DIAGRAM: layout(toFretDiagram(item), ctx);
     162            0 :         break;
     163            0 :     case ElementType::FSYMBOL:      layout(toFSymbol(item), ctx);
     164            0 :         break;
     165            0 :     case ElementType::GLISSANDO:    layout(toGlissando(item), ctx);
     166            0 :         break;
     167            0 :     case ElementType::GUITAR_BEND:  layout(toGuitarBend(item), ctx);
     168            0 :         break;
     169            0 :     case ElementType::GRADUAL_TEMPO_CHANGE: layout(toGradualTempoChange(item), ctx);
     170            0 :         break;
     171            0 :     case ElementType::HAIRPIN:      layout(toHairpin(item), ctx);
     172            0 :         break;
     173            0 :     case ElementType::HAMMER_ON_PULL_OFF: layout(toHammerOnPullOff(item), ctx);
     174            0 :         break;
     175            0 :     case ElementType::HARP_DIAGRAM: layout(toHarpPedalDiagram(item), ctx);
     176            0 :         break;
     177            0 :     case ElementType::IMAGE:        layout(toImage(item), ctx);
     178            0 :         break;
     179            0 :     case ElementType::INSTRUMENT_CHANGE: layout(toInstrumentChange(item), ctx);
     180            0 :         break;
     181            0 :     case ElementType::JUMP:         layout(toJump(item), ctx);
     182            0 :         break;
     183            0 :     case ElementType::KEYSIG:       layout(toKeySig(item), ctx);
     184            0 :         break;
     185            0 :     case ElementType::LAYOUT_BREAK: layout(toLayoutBreak(item), ctx);
     186            0 :         break;
     187            0 :     case ElementType::LET_RING:     layout(toLetRing(item), ctx);
     188            0 :         break;
     189            0 :     case ElementType::LYRICS:       layout(toLyrics(item), ctx);
     190            0 :         break;
     191            0 :     case ElementType::MARKER:       layout(toMarker(item), ctx);
     192            0 :         break;
     193            0 :     case ElementType::MEASURE_NUMBER: layout(toMeasureNumber(item), ctx);
     194            0 :         break;
     195            0 :     case ElementType::MEASURE_REPEAT: layout(toMeasureRepeat(item), ctx);
     196            0 :         break;
     197            0 :     case ElementType::NOTEHEAD:     layout(toNoteHead(item), ctx);
     198            0 :         break;
     199            0 :     case ElementType::NOTELINE:     layout(toNoteLine(item), ctx);
     200            0 :         break;
     201            0 :     case ElementType::OTTAVA:       layout(toOttava(item), ctx);
     202            0 :         break;
     203            0 :     case ElementType::ORNAMENT:     layout(toOrnament(item), ctx);
     204            0 :         break;
     205            0 :     case ElementType::PALM_MUTE:    layout(toPalmMute(item), ctx);
     206            0 :         break;
     207            0 :     case ElementType::PEDAL:        layout(toPedal(item), ctx);
     208            0 :         break;
     209            0 :     case ElementType::PLAY_COUNT_TEXT: layout(toPlayCountText(item), ctx);
     210            0 :         break;
     211            0 :     case ElementType::PLAYTECH_ANNOTATION: layout(toPlayTechAnnotation(item), ctx);
     212            0 :         break;
     213            0 :     case ElementType::REHEARSAL_MARK: layout(toRehearsalMark(item), ctx);
     214            0 :         break;
     215            0 :     case ElementType::SLUR:         layout(toSlur(item), ctx);
     216            0 :         break;
     217            0 :     case ElementType::SPACER:       layout(toSpacer(item), ctx);
     218            0 :         break;
     219            0 :     case ElementType::STAFF_TEXT:   layout(toStaffText(item), ctx);
     220            0 :         break;
     221            0 :     case ElementType::STAVE_SHARING_LABEL: layout(toStaveSharingLabel(item), ctx);
     222            0 :         break;
     223            0 :     case ElementType::STAFFTYPE_CHANGE: layout(toStaffTypeChange(item), ctx);
     224            0 :         break;
     225            0 :     case ElementType::STICKING:     layout(toSticking(item), ctx);
     226            0 :         break;
     227            0 :     case ElementType::STRING_TUNINGS: layout(toStringTunings(item), ctx);
     228            0 :         break;
     229            0 :     case ElementType::SYMBOL:       layout(toSymbol(item), ctx);
     230            0 :         break;
     231            0 :     case ElementType::SYSTEM_TEXT:  layout(toSystemText(item), ctx);
     232            0 :         break;
     233            0 :     case ElementType::SOUND_FLAG:   layout(item_cast<SoundFlag*>(item), ctx);
     234            0 :         break;
     235            0 :     case ElementType::TAPPING:      layout(toTapping(item), ctx);
     236            0 :         break;
     237            0 :     case ElementType::TEMPO_TEXT:   layout(toTempoText(item), ctx);
     238            0 :         break;
     239            0 :     case ElementType::TEXT:         layout(toText(item), ctx);
     240            0 :         break;
     241            0 :     case ElementType::TEXTLINE:     layout(toTextLine(item), ctx);
     242            0 :         break;
     243            0 :     case ElementType::TIMESIG:      layout(toTimeSig(item), ctx);
     244            0 :         break;
     245            0 :     case ElementType::TREMOLO_SINGLECHORD: layout(item_cast<TremoloSingleChord*>(item), ctx);
     246            0 :         break;
     247            0 :     case ElementType::TREMOLO_TWOCHORD:    layout(item_cast<TremoloTwoChord*>(item), ctx);
     248            0 :         break;
     249            0 :     case ElementType::TREMOLOBAR:   layout(toTremoloBar(item), ctx);
     250            0 :         break;
     251            0 :     case ElementType::TRILL:        layout(toTrill(item), ctx);
     252            0 :         break;
     253            0 :     case ElementType::VIBRATO:      layout(toVibrato(item), ctx);
     254            0 :         break;
     255            0 :     case ElementType::VOLTA:        layout(toVolta(item), ctx);
     256            0 :         break;
     257            0 :     case ElementType::WHAMMY_BAR:   layout(toWhammyBar(item), ctx);
     258            0 :         break;
     259              :     // drumset
     260            0 :     case ElementType::CHORD:        layout(toChord(item), ctx);
     261            0 :         break;
     262            0 :     case ElementType::STEM:         layout(toStem(item), ctx);
     263            0 :         break;
     264            0 :     default:
     265            0 :         LOGE() << "Not handled: " << item->typeName();
     266            0 :         IF_ASSERT_FAILED(false) {
     267              :             LayoutContext tctx(item->score());
     268              :             TLayout::layoutItem(item, tctx);
     269              :         }
     270              :         break;
     271              :     }
     272            0 : }
     273              : 
     274            0 : void SingleLayout::layoutLineSegment(LineSegment* item, const Context& ctx)
     275              : {
     276            0 :     switch (item->type()) {
     277            0 :     case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT: layout(toGradualTempoChangeSegment(item), ctx);
     278            0 :         break;
     279            0 :     case ElementType::HAIRPIN_SEGMENT:   layout(toHairpinSegment(item), ctx);
     280            0 :         break;
     281            0 :     case ElementType::LET_RING_SEGMENT:  layout(toLetRingSegment(item), ctx);
     282            0 :         break;
     283            0 :     case ElementType::OTTAVA_SEGMENT:    layout(toOttavaSegment(item), ctx);
     284            0 :         break;
     285            0 :     case ElementType::PALM_MUTE_SEGMENT: layout(toPalmMuteSegment(item), ctx);
     286            0 :         break;
     287            0 :     case ElementType::PEDAL_SEGMENT:     layout(toPedalSegment(item), ctx);
     288            0 :         break;
     289            0 :     case ElementType::TEXTLINE_SEGMENT:  layout(toTextLineSegment(item), ctx);
     290            0 :         break;
     291            0 :     case ElementType::TRILL_SEGMENT:     layout(toTrillSegment(item), ctx);
     292            0 :         break;
     293            0 :     case ElementType::VIBRATO_SEGMENT:   layout(toVibratoSegment(item), ctx);
     294            0 :         break;
     295            0 :     case ElementType::VOLTA_SEGMENT:     layout(toVoltaSegment(item), ctx);
     296            0 :         break;
     297            0 :     case ElementType::WHAMMY_BAR_SEGMENT: layout(toWhammyBarSegment(item), ctx);
     298            0 :         break;
     299            0 :     default:
     300            0 :         UNREACHABLE;
     301              :         break;
     302              :     }
     303            0 : }
     304              : 
     305            0 : const MStyle& SingleLayout::Context::style() const
     306              : {
     307            0 :     return m_score->style();
     308              : }
     309              : 
     310            0 : std::shared_ptr<IEngravingFont> SingleLayout::Context::engravingFont() const
     311              : {
     312            0 :     return m_score->engravingFont();
     313              : }
     314              : 
     315            0 : DummyParent* SingleLayout::Context::dummyParent() const
     316              : {
     317            0 :     return m_score->dummy();
     318              : }
     319              : 
     320            0 : void SingleLayout::layout(Accidental* item, const Context&)
     321              : {
     322            0 :     if (!item->ldata() || !item->ldata()->isValid()) {
     323            0 :         Accidental::LayoutData* ldata = item->mutldata();
     324            0 :         SymId symId = item->symId();
     325            0 :         Accidental::LayoutData::Sym s(symId, 0.0, 0.0);
     326            0 :         ldata->syms.push_back(s);
     327            0 :         ldata->setBbox(item->symBbox(symId));
     328              :     }
     329            0 : }
     330              : 
     331            0 : void SingleLayout::layout(ActionIcon* item, const Context&)
     332              : {
     333            0 :     FontMetrics fontMetrics(item->iconFont());
     334            0 :     item->setbbox(fontMetrics.boundingRect(Char(item->icon())));
     335            0 : }
     336              : 
     337            0 : void SingleLayout::layout(Ambitus* item, const Context& ctx)
     338              : {
     339            0 :     Ambitus::LayoutData* ldata = item->mutldata();
     340            0 :     double headWdt = item->headWidth();
     341            0 :     double spatium = item->spatium();
     342              : 
     343            0 :     double lineDist = spatium;
     344            0 :     constexpr int numOfLines = 3;
     345              : 
     346              :     //
     347              :     // NOTEHEADS Y POS
     348              :     //
     349              :     // if pitch == INVALID_PITCH or tpc == Tpc::TPC_INVALID, set to some default:
     350              :     // for use in palettes and when actual range cannot be calculated (new ambitus or no notes in staff)
     351              :     //
     352              : 
     353              :     // top notehead
     354            0 :     ldata->topPos.setY(0.0);
     355              : 
     356              :     // bottom notehead
     357            0 :     ldata->bottomPos.setY((numOfLines - 1) * lineDist);
     358              : 
     359              :     //
     360              :     // NOTEHEAD X POS
     361              :     //
     362              :     // Note: manages colliding accidentals
     363              :     //
     364              : 
     365            0 :     layout(item->topAccidental(), ctx);
     366            0 :     layout(item->bottomAccidental(), ctx);
     367              : 
     368            0 :     double accNoteDist = item->absoluteFromSpatium(ctx.style().styleS(Sid::accidentalNoteDistance));
     369            0 :     double xAccidOffTop = item->topAccidental()->width(LD_ACCESS::BAD) + accNoteDist;
     370            0 :     double xAccidOffBottom = item->bottomAccidental()->width(LD_ACCESS::BAD) + accNoteDist;
     371              : 
     372            0 :     switch (item->direction()) {
     373            0 :     case DirectionH::AUTO:                       // noteheads one above the other
     374              :         // left align noteheads and right align accidentals 'hanging' on the left
     375            0 :         ldata->topPos.setX(0.0);
     376            0 :         ldata->bottomPos.setX(0.0);
     377            0 :         item->topAccidental()->mutldata()->setPosX(-xAccidOffTop);
     378            0 :         item->bottomAccidental()->mutldata()->setPosX(-xAccidOffBottom);
     379            0 :         break;
     380            0 :     case DirectionH::LEFT:                       // top notehead at the left of bottom notehead
     381              :         // place top notehead at left margin; bottom notehead at right of top head;
     382              :         // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
     383            0 :         ldata->topPos.setX(0.0);
     384            0 :         ldata->bottomPos.setX(headWdt);
     385            0 :         item->topAccidental()->mutldata()->setPosX(-xAccidOffTop);
     386            0 :         item->bottomAccidental()->mutldata()->setPosX(headWdt - xAccidOffBottom);
     387            0 :         break;
     388            0 :     case DirectionH::RIGHT:                      // top notehead at the right of bottom notehead
     389              :         // bottom notehead at left margin; top notehead at right of bottomnotehead
     390              :         // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
     391            0 :         ldata->bottomPos.setX(0.0);
     392            0 :         ldata->topPos.setX(headWdt);
     393            0 :         item->bottomAccidental()->mutldata()->setPosX(-xAccidOffBottom);
     394            0 :         item->topAccidental()->mutldata()->setPosX(headWdt - xAccidOffTop);
     395            0 :         break;
     396              :     }
     397              : 
     398              :     // compute line from top note centre to bottom note centre
     399            0 :     LineF fullLine(ldata->topPos.x() + headWdt * 0.5,
     400              :                    ldata->topPos.y(),
     401            0 :                    ldata->bottomPos.x() + headWdt * 0.5,
     402            0 :                    ldata->bottomPos.y());
     403              :     // shorten line on each side by offsets
     404            0 :     double yDelta = ldata->bottomPos.y() - ldata->topPos.y();
     405            0 :     if (!RealIsNull(yDelta)) {
     406            0 :         double off = Ambitus::LINEOFFSET_DEFAULT.toAbsolute(spatium);
     407            0 :         PointF p1 = fullLine.pointAt(off / yDelta);
     408            0 :         PointF p2 = fullLine.pointAt(1 - (off / yDelta));
     409            0 :         ldata->line = LineF(p1, p2);
     410              :     } else {
     411            0 :         ldata->line = fullLine;
     412              :     }
     413              : 
     414            0 :     RectF headRect(0, -0.5 * spatium, headWdt, 1 * spatium);
     415            0 :     ldata->setBbox(headRect.translated(ldata->topPos).united(headRect.translated(ldata->bottomPos))
     416            0 :                    .united(item->topAccidental()->ldata()->bbox(LD_ACCESS::BAD)
     417            0 :                            .translated(item->topAccidental()->ldata()->pos()))
     418            0 :                    .united(item->bottomAccidental()->ldata()->bbox(LD_ACCESS::BAD)
     419            0 :                            .translated(item->bottomAccidental()->ldata()->pos()))
     420              :                    );
     421            0 : }
     422              : 
     423            0 : void SingleLayout::layout(Arpeggio* item, const Context& ctx)
     424              : {
     425            0 :     Arpeggio::LayoutData* ldata = item->mutldata();
     426              : 
     427            0 :     auto symbolLine = [](const std::shared_ptr<const IEngravingFont>& f, Arpeggio::LayoutData* data, SymId end, SymId fill)
     428              :     {
     429            0 :         data->symbols.clear();
     430              : 
     431            0 :         double w = data->bottom - data->top;
     432            0 :         double w1 = f->advance(end, data->magS);
     433            0 :         double w2 = f->advance(fill, data->magS);
     434            0 :         int n = lrint((w - w1) / w2);
     435            0 :         for (int i = 0; i < n; ++i) {
     436            0 :             data->symbols.push_back(fill);
     437              :         }
     438            0 :         data->symbols.push_back(end);
     439            0 :     };
     440              : 
     441            0 :     ldata->arpeggioHeight = item->spatium() * 4;
     442            0 :     ldata->top = 0.0;
     443            0 :     ldata->bottom = ldata->arpeggioHeight;
     444              : 
     445            0 :     ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
     446            0 :     ldata->magS = ldata->mag() * (ctx.style().spatium() / ctx.style().defaultSpatium());
     447              : 
     448            0 :     std::shared_ptr<const IEngravingFont> font = ctx.engravingFont();
     449            0 :     switch (item->arpeggioType()) {
     450            0 :     case ArpeggioType::NORMAL: {
     451            0 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUp, SymId::wiggleArpeggiatoUp);
     452              :         // string is rotated -90 degrees
     453            0 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     454            0 :         ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     455            0 :     } break;
     456              : 
     457            0 :     case ArpeggioType::UP: {
     458            0 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
     459              :         // string is rotated -90 degrees
     460            0 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     461            0 :         ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     462            0 :     } break;
     463              : 
     464            0 :     case ArpeggioType::DOWN: {
     465            0 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
     466              :         // string is rotated +90 degrees (so that UpArrow turns into a DownArrow)
     467            0 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     468            0 :         ldata->setBbox(RectF(0.0, ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     469            0 :     } break;
     470              : 
     471            0 :     case ArpeggioType::UP_STRAIGHT: {
     472            0 :         double x1 = item->spatium() * 0.5;
     473            0 :         ldata->symsBBox = font->bbox(SymId::arrowheadBlackUp, ldata->magS);
     474            0 :         double w = ldata->symsBBox.width();
     475            0 :         ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
     476            0 :     } break;
     477              : 
     478            0 :     case ArpeggioType::DOWN_STRAIGHT: {
     479            0 :         double x1 = item->spatium() * 0.5;
     480            0 :         ldata->symsBBox = font->bbox(SymId::arrowheadBlackDown, ldata->magS);
     481            0 :         double w = ldata->symsBBox.width();
     482            0 :         ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
     483            0 :     } break;
     484              : 
     485            0 :     case ArpeggioType::BRACKET: {
     486            0 :         double w  = ctx.style().styleS(Sid::arpeggioHookLen).val() * item->spatium();
     487            0 :         ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
     488            0 :     } break;
     489              :     }
     490            0 : }
     491              : 
     492            0 : void SingleLayout::layout(ChordBracket* item, const Context& ctx)
     493              : {
     494            0 :     ChordBracket::LayoutData* ldata = item->mutldata();
     495              : 
     496            0 :     ldata->arpeggioHeight = item->spatium() * 2;
     497            0 :     ldata->top = 0.0;
     498            0 :     ldata->bottom = ldata->arpeggioHeight;
     499            0 :     ldata->setMag(1);
     500            0 :     ldata->magS = 1;
     501              : 
     502            0 :     double w  = item->absoluteFromSpatium(ctx.style().styleS(Sid::chordBracketHookLen));
     503            0 :     ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
     504            0 : }
     505              : 
     506            0 : void SingleLayout::layout(Articulation* item, const Context& ctx)
     507              : {
     508            0 :     RectF bbox;
     509              : 
     510            0 :     if (item->textType() != ArticulationTextType::NO_TEXT) {
     511            0 :         if (!item->text()) {
     512            0 :             Text* text = new Text(item, TextStyleType::ARTICULATION);
     513            0 :             static const ElementStyle elementStyle = {};
     514            0 :             text->initElementStyle(&elementStyle);
     515            0 :             item->setText(text);
     516              :         }
     517              : 
     518            0 :         Text* text = item->text();
     519            0 :         text->setXmlText(TConv::text(item->textType()));
     520            0 :         text->setTrack(item->track());
     521            0 :         text->setOwnershipParent(item);
     522              : 
     523            0 :         layoutTextBase(item->text(), ctx, item->text()->mutldata());
     524            0 :         bbox = text->ldata()->bbox();
     525              :     } else {
     526            0 :         bbox = item->symBbox(item->symId());
     527            0 :         bbox.translate(-0.5 * bbox.width(), 0.0);
     528              :     }
     529              : 
     530            0 :     item->setbbox(bbox);
     531            0 : }
     532              : 
     533            0 : void SingleLayout::layout(BagpipeEmbellishment* item, const Context& ctx)
     534              : {
     535            0 :     BagpipeEmbellishment::LayoutData* ldata = item->mutldata();
     536            0 :     const double mags = item->magS() * 0.75; // grace head magnification
     537            0 :     const double spatium = ctx.style().spatium();
     538              : 
     539            0 :     const BagpipeNoteList nl = item->resolveNoteList();
     540              : 
     541            0 :     ldata->clearBbox();
     542            0 :     ldata->isDrawBeam = nl.size() > 1;
     543            0 :     ldata->isDrawFlag = nl.size() == 1;
     544            0 :     ldata->spatium = spatium;
     545            0 :     ldata->headsym = SymId::noteheadBlack;
     546            0 :     ldata->flagsym = SymId::flag32ndUp;
     547            0 :     ldata->stemLineW = ldata->spatium * 0.1;
     548              : 
     549            0 :     BagpipeEmbellishment::LayoutData::BeamData& dataBeam = ldata->beamData;
     550            0 :     dataBeam.y = (-8 * spatium / 2);
     551            0 :     dataBeam.width = (0.3 * spatium);
     552              : 
     553            0 :     const RectF headBBox = ctx.engravingFont()->bbox(ldata->headsym, mags);
     554            0 :     const RectF flagBBox = ctx.engravingFont()->bbox(ldata->flagsym, mags);
     555            0 :     const double xcorr = spatium * 0.1;     // correction to align flag with top of stem
     556              : 
     557            0 :     double headW = headBBox.width();
     558            0 :     const double headw = 1.2 * headW;       // grace head width
     559            0 :     const double headp = 1.6 * headW;       // horizontal head pitch
     560            0 :     const double xl = (1 - 1.6 * (nl.size() - 1)) * headW / 2; // calc x for stem of leftmost note
     561              : 
     562              :     // draw the notes including stem, (optional) flag and (optional) ledger line
     563            0 :     double x = xl;
     564            0 :     for (size_t i = 0; i < nl.size(); ++i) {
     565            0 :         BagpipeEmbellishment::LayoutData::NoteData& noteData = ldata->notesData[i];
     566            0 :         const int line = BagpipeEmbellishment::BAGPIPE_NOTEINFO_LIST[nl.at(i)].line;
     567            0 :         const double y1f = ((line - 6) * spatium / 2);      // top of stem for note with flag
     568            0 :         const double y2 = (line * spatium / 2);             // bottom of stem
     569            0 :         const double ycorr = (0.8 * spatium);               // correction to align flag with top of stem
     570              : 
     571              :         // head
     572            0 :         noteData.headXY = PointF(x - headw, y2);
     573            0 :         ldata->addBbox(headBBox.translated(noteData.headXY));
     574              : 
     575              :         // stem
     576              :         // top of stems actually used
     577            0 :         double y1 = ldata->isDrawFlag ? y1f : dataBeam.y;
     578            0 :         noteData.stemLine = LineF(x - ldata->stemLineW * .5, y1, x - ldata->stemLineW * .5, y2);
     579            0 :         ldata->addBbox(RectF(x - ldata->stemLineW * .5 - headw, y1, ldata->stemLineW, y2 - y1));
     580              : 
     581              :         // flag
     582            0 :         if (ldata->isDrawFlag) {
     583            0 :             noteData.flagXY = PointF(x - ldata->stemLineW * .5 + xcorr, y1 + ycorr);
     584            0 :             ldata->addBbox(flagBBox.translated(noteData.flagXY));
     585              :         }
     586              : 
     587              :         // draw the ledger line for high A
     588            0 :         if (line == -2) {
     589            0 :             noteData.ledgerLine = LineF(x - headw * 1.5 - ldata->stemLineW * .5, y2, x + headw * .5 - ldata->stemLineW * .5, y2);
     590            0 :             ldata->addBbox(RectF(x - headw * 1.5 - ldata->stemLineW * .5, y2 - ldata->stemLineW * 2, headw * 2, ldata->stemLineW));
     591              :         }
     592              : 
     593              :         // move x to next note x position
     594            0 :         x += headp;
     595              :     }
     596              : 
     597            0 :     dataBeam.x1 = xl - ldata->stemLineW * .5;
     598            0 :     dataBeam.x2 = x - headp - ldata->stemLineW * .5;
     599            0 : }
     600              : 
     601            0 : void SingleLayout::layout(BarLine* item, const Context& ctx)
     602              : {
     603            0 :     BarLine::LayoutData* ldata = item->mutldata();
     604            0 :     ldata->setPos(PointF());
     605            0 :     ldata->setMag(1.0);
     606              : 
     607            0 :     double spatium = item->spatium();
     608            0 :     ldata->y1 = (spatium * .5 * item->spanFrom());
     609            0 :     if (muse::RealIsEqual(ldata->y2, 0.0)) {
     610            0 :         ldata->y2 = (spatium * .5 * (8.0 + item->spanTo()));
     611              :     }
     612              : 
     613            0 :     auto layoutWidth = [](BarLine* item, const Context& ctx) {
     614            0 :         const double dotWidth = item->symWidth(SymId::repeatDot);
     615              : 
     616            0 :         double w = 0.0;
     617            0 :         switch (item->barLineType()) {
     618            0 :         case BarLineType::DOUBLE:
     619            0 :             w = ctx.style().styleAbsolute(Sid::doubleBarWidth) * 2.0 + ctx.style().styleAbsolute(Sid::doubleBarDistance);
     620            0 :             break;
     621            0 :         case BarLineType::DOUBLE_HEAVY:
     622            0 :             w = ctx.style().styleAbsolute(Sid::endBarWidth) * 2.0 + ctx.style().styleAbsolute(Sid::endBarDistance);
     623            0 :             break;
     624            0 :         case BarLineType::END_START_REPEAT:
     625            0 :             w = ctx.style().styleAbsolute(Sid::endBarWidth)
     626            0 :                 + ctx.style().styleAbsolute(Sid::barWidth) * 2.0
     627            0 :                 + ctx.style().styleAbsolute(Sid::endBarDistance) * 2.0
     628            0 :                 + ctx.style().styleAbsolute(Sid::repeatBarlineDotSeparation) * 2.0
     629            0 :                 + dotWidth * 2;
     630            0 :             break;
     631            0 :         case BarLineType::START_REPEAT:
     632              :         case BarLineType::END_REPEAT:
     633            0 :             w = ctx.style().styleAbsolute(Sid::endBarWidth)
     634            0 :                 + ctx.style().styleAbsolute(Sid::barWidth)
     635            0 :                 + ctx.style().styleAbsolute(Sid::endBarDistance)
     636            0 :                 + ctx.style().styleAbsolute(Sid::repeatBarlineDotSeparation)
     637              :                 + dotWidth;
     638            0 :             break;
     639            0 :         case BarLineType::END:
     640              :         case BarLineType::REVERSE_END:
     641            0 :             w = ctx.style().styleAbsolute(Sid::endBarWidth)
     642            0 :                 + ctx.style().styleAbsolute(Sid::barWidth)
     643            0 :                 + ctx.style().styleAbsolute(Sid::endBarDistance);
     644            0 :             break;
     645            0 :         case BarLineType::BROKEN:
     646            0 :             w = ctx.style().styleAbsolute(Sid::dashBarWidth);
     647            0 :             break;
     648            0 :         case BarLineType::NORMAL:
     649              :         case BarLineType::DOTTED:
     650            0 :             w = ctx.style().styleAbsolute(Sid::barWidth);
     651            0 :             break;
     652            0 :         case BarLineType::HEAVY:
     653            0 :             w = ctx.style().styleAbsolute(Sid::endBarWidth);
     654            0 :             break;
     655              :         }
     656            0 :         return w;
     657              :     };
     658              : 
     659            0 :     double w = layoutWidth(item, ctx) * ldata->mag();
     660            0 :     RectF bbox(0.0, ldata->y1, w, ldata->y2 - ldata->y1);
     661            0 :     ldata->setBbox(bbox);
     662            0 : }
     663              : 
     664            0 : void SingleLayout::layout(Bend* item, const Context&)
     665              : {
     666            0 :     Bend::LayoutData* ldata = item->mutldata();
     667            0 :     double spatium = item->spatium();
     668            0 :     double lw = item->absoluteFromSpatium(item->lineWidth());
     669              : 
     670            0 :     ldata->noteWidth = 0.0;
     671            0 :     ldata->notePos = PointF();
     672              : 
     673            0 :     RectF bb;
     674              : 
     675            0 :     FontMetrics fm(item->font(spatium));
     676              : 
     677            0 :     size_t n   = item->points().size();
     678            0 :     double x = ldata->noteWidth;
     679            0 :     double y = -spatium * .8;
     680              :     double x2, y2;
     681              : 
     682            0 :     double aw = spatium * .5;
     683            0 :     PolygonF arrowUp;
     684            0 :     arrowUp << PointF(0, 0) << PointF(aw * .5, aw) << PointF(-aw * .5, aw);
     685            0 :     PolygonF arrowDown;
     686            0 :     arrowDown << PointF(0, 0) << PointF(aw * .5, -aw) << PointF(-aw * .5, -aw);
     687              : 
     688            0 :     for (size_t pt = 0; pt < n; ++pt) {
     689            0 :         if (pt == (n - 1)) {
     690            0 :             break;
     691              :         }
     692            0 :         int pitch = item->points().at(pt).pitch;
     693            0 :         if (pt == 0 && pitch) {
     694            0 :             y2 = -ldata->notePos.y() - spatium * 2;
     695            0 :             x2 = x;
     696            0 :             bb.unite(RectF(x, y, x2 - x, y2 - y));
     697              : 
     698            0 :             bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
     699              : 
     700            0 :             int idx = (pitch + 12) / 25;
     701            0 :             const char* l = Bend::label[idx];
     702            0 :             bb.unite(fm.boundingRect(String::fromAscii(l)));
     703            0 :             y = y2;
     704              :         }
     705            0 :         if (pitch == item->points().at(pt + 1).pitch) {
     706            0 :             if (pt == (n - 2)) {
     707            0 :                 break;
     708              :             }
     709            0 :             x2 = x + spatium;
     710            0 :             y2 = y;
     711            0 :             bb.unite(RectF(x, y, x2 - x, y2 - y));
     712            0 :         } else if (pitch < item->points().at(pt + 1).pitch) {
     713              :             // up
     714            0 :             x2 = x + spatium * .5;
     715            0 :             y2 = -ldata->notePos.y() - spatium * 2;
     716            0 :             double dx = x2 - x;
     717            0 :             double dy = y2 - y;
     718              : 
     719            0 :             PainterPath path;
     720            0 :             path.moveTo(x, y);
     721            0 :             path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
     722            0 :             bb.unite(path.boundingRect());
     723            0 :             bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
     724              : 
     725            0 :             int idx = (item->points().at(pt + 1).pitch + 12) / 25;
     726            0 :             const char* l = Bend::label[idx];
     727            0 :             bb.unite(fm.boundingRect(String::fromAscii(l)));
     728            0 :         } else {
     729              :             // down
     730            0 :             x2 = x + spatium * .5;
     731            0 :             y2 = y + spatium * 3;
     732            0 :             double dx = x2 - x;
     733            0 :             double dy = y2 - y;
     734              : 
     735            0 :             PainterPath path;
     736            0 :             path.moveTo(x, y);
     737            0 :             path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
     738            0 :             bb.unite(path.boundingRect());
     739              : 
     740            0 :             bb.unite(arrowDown.translated(x2, y2 - spatium * .2).boundingRect());
     741            0 :         }
     742            0 :         x = x2;
     743            0 :         y = y2;
     744              :     }
     745            0 :     bb.adjust(-lw, -lw, lw, lw);
     746            0 :     ldata->setBbox(bb);
     747            0 :     ldata->setPos(0.0, 0.0);
     748            0 : }
     749              : 
     750            0 : void SingleLayout::layout(Bracket* item, const Context& ctx)
     751              : {
     752            0 :     Bracket::LayoutData* ldata = item->mutldata();
     753              : 
     754            0 :     ldata->bracketHeight = 3.5 * item->spatium() * 2;
     755            0 :     ldata->braceSymbol = item->braceSymbol();
     756              : 
     757            0 :     Shape shape;
     758              : 
     759            0 :     switch (item->bracketType()) {
     760            0 :     case BracketType::BRACE: {
     761            0 :         if (item->braceSymbol() == SymId::noSym) {
     762            0 :             ldata->braceSymbol = SymId::brace;
     763              :         }
     764            0 :         double h = ldata->bracketHeight;
     765            0 :         double w = item->symWidth(ldata->braceSymbol) * item->magx();
     766            0 :         ldata->setBbox(RectF(0, 0, w, h));
     767            0 :         ldata->shape.add(ldata->bbox());
     768            0 :         ldata->bracketWidth = w + ctx.style().styleAbsolute(Sid::akkoladeBarDistance);
     769              :     }
     770            0 :     break;
     771            0 :     case BracketType::NORMAL: {
     772            0 :         double spatium = item->spatium();
     773            0 :         double w = ctx.style().styleAbsolute(Sid::bracketWidth) * 0.5;
     774            0 :         double x = -w;
     775              : 
     776            0 :         double bd = spatium * 0.5;
     777            0 :         shape.add(RectF(x, -bd, w * 2, 2 * (item->ldata()->h2() + bd)));
     778            0 :         shape.add(item->symBbox(SymId::bracketTop).translated(PointF(-w, -bd)));
     779            0 :         shape.add(item->symBbox(SymId::bracketBottom).translated(PointF(-w, bd + 2 * item->ldata()->h2())));
     780              : 
     781            0 :         w += item->symWidth(SymId::bracketTop);
     782            0 :         double y = -item->symHeight(SymId::bracketTop) - bd;
     783            0 :         double h = (-y + item->ldata()->h2()) * 2;
     784            0 :         ldata->setBbox(x, y, w, h);
     785              : 
     786            0 :         ldata->bracketWidth = ctx.style().styleAbsolute(Sid::bracketWidth) + ctx.style().styleAbsolute(Sid::bracketDistance);
     787              :     }
     788            0 :     break;
     789            0 :     case BracketType::SQUARE:
     790              :     {
     791            0 :         double w = ctx.style().styleAbsolute(Sid::staffLineWidth) * .5;
     792            0 :         double x = -w;
     793            0 :         double y = -w;
     794            0 :         double h = (item->ldata()->h2() + w) * 2;
     795            0 :         w += (0.5 * item->spatium() + 3 * w);
     796            0 :         ldata->setBbox(x, y, w, h);
     797            0 :         shape.add(item->ldata()->bbox());
     798              : 
     799            0 :         ldata->bracketWidth = ctx.style().styleAbsolute(Sid::staffLineWidth) / 2 + 0.5 * item->spatium();
     800              :     }
     801            0 :     break;
     802            0 :     case BracketType::LINE: {
     803            0 :         double spatium = item->spatium();
     804            0 :         double w = 0.67 * ctx.style().styleAbsolute(Sid::bracketWidth) * 0.5;
     805            0 :         double x = -w;
     806            0 :         double bd = spatium * 0.25;
     807            0 :         double y = -bd;
     808            0 :         double h = (-y + item->ldata()->h2()) * 2;
     809            0 :         ldata->setBbox(x, y, w, h);
     810            0 :         shape.add(item->ldata()->bbox());
     811              : 
     812            0 :         ldata->bracketWidth = 0.67 * ctx.style().styleAbsolute(Sid::bracketWidth) + ctx.style().styleAbsolute(Sid::bracketDistance);
     813              :     }
     814            0 :     break;
     815            0 :     case BracketType::GROUP:
     816            0 :         layoutGroupBracket(item, ctx);
     817            0 :         break;
     818            0 :     case BracketType::NO_BRACKET:
     819            0 :         break;
     820              :     }
     821              : 
     822            0 :     ldata->shape = shape;
     823            0 : }
     824              : 
     825            0 : void SingleLayout::layoutGroupBracket(Bracket* item, const Context& ctx)
     826              : {
     827            0 :     Bracket::LayoutData* ldata = item->mutldata();
     828            0 :     ldata->bracketHeight = 12 * item->spatium();
     829            0 :     double w = ctx.style().styleAbsolute(Sid::staffLineWidth) * 0.5;
     830            0 :     double x = 0.0;
     831            0 :     double y = -w;
     832            0 :     double h = (ldata->bracketHeight * 0.5 + w) * 2;
     833            0 :     double width = item->spatium();
     834            0 :     ldata->setBbox(RectF(x, y, width, h));
     835            0 :     ldata->shape.add(ldata->bbox());
     836              : 
     837            0 :     ldata->bracketWidth = width;
     838              : 
     839            0 :     if (!item->text()) {
     840            0 :         const_cast<Bracket*>(item)->setText(new Text(const_cast<Bracket*>(item)));
     841              :     }
     842              : 
     843            0 :     Text* text = item->text();
     844            0 :     text->setAlign(Align(AlignH::HCENTER, AlignV::VCENTER));
     845            0 :     text->setPosition(AlignH::HCENTER);
     846            0 :     text->setTextAngle(-90);
     847            0 :     text->setXmlText(item->bracketItem()->longName());
     848            0 :     layout(text, ctx);
     849            0 :     text->mutldata()->setPos(0.0, item->ldata()->bbox().height() / 2);
     850              : 
     851            0 :     double textPadding = 0.5 * item->spatium();
     852            0 :     RectF mask = text->ldata()->bbox().translated(text->pos()).padded(textPadding);
     853            0 :     ldata->setMask(mask);
     854            0 : }
     855              : 
     856            0 : void SingleLayout::layout(Breath* item, const Context&)
     857              : {
     858            0 :     item->setbbox(item->symBbox(item->symId()));
     859            0 : }
     860              : 
     861            0 : void SingleLayout::layout(Capo* item, const Context& ctx)
     862              : {
     863            0 :     layoutTextBase(item, ctx, item->mutldata());
     864            0 : }
     865              : 
     866            0 : void SingleLayout::layout(Chord* item, const Context& ctx)
     867              : {
     868            0 :     LayoutContext tctx(ctx.dontUseScore());
     869            0 :     ChordLayout::computeUp(item, tctx);
     870            0 :     ChordLayout::layout(item, tctx);
     871            0 :     ChordLayout::layoutStem(item, tctx);
     872            0 :     ChordLayout::layoutLedgerLines({ item }, tctx);
     873            0 : }
     874              : 
     875            0 : void SingleLayout::layout(ChordLine* item, const Context& ctx)
     876              : {
     877            0 :     ChordLine::LayoutData* ldata = item->mutldata();
     878            0 :     ldata->setMag(1.0);
     879            0 :     TLayout::layoutChordLinePath(item, ldata, item->spatium(), item->chordMag());
     880              : 
     881            0 :     ldata->setPos(0.0, 0.0);
     882              : 
     883            0 :     if (!item->isWavy()) {
     884            0 :         RectF r = ldata->path.boundingRect();
     885            0 :         int x1 = 0, y1 = 0, width = 0, height = 0;
     886              : 
     887            0 :         x1 = r.x();
     888            0 :         y1 = r.y();
     889            0 :         width = r.width();
     890            0 :         height = r.height();
     891            0 :         ldata->setBbox(x1, y1, width, height);
     892              :     } else {
     893            0 :         RectF r = ctx.engravingFont()->bbox(item->waveSym(), item->magS());
     894              : 
     895            0 :         ldata->setBbox(r);
     896              :     }
     897            0 : }
     898              : 
     899            0 : void SingleLayout::layout(Clef* item, const Context& ctx)
     900              : {
     901            0 :     Clef::LayoutData* ldata = item->mutldata();
     902            0 :     constexpr int lines = 5;
     903            0 :     constexpr Spatium lineDist = 1.0_sp;
     904            0 :     double spatium = ctx.style().spatium();
     905            0 :     Spatium yoff = 0.0_sp;
     906              : 
     907            0 :     if (item->clefType() != ClefType::INVALID && item->clefType() != ClefType::MAX) {
     908            0 :         ldata->symId = ClefInfo::symId(item->clefType());
     909            0 :         yoff = lineDist * (5 - ClefInfo::line(item->clefType()));
     910              :     } else {
     911            0 :         ldata->symId = SymId::noSym;
     912              :     }
     913              : 
     914            0 :     switch (item->clefType()) {
     915            0 :     case ClefType::C_19C:                                    // 19th C clef is like a G clef
     916            0 :         yoff = lineDist * 1.5;
     917            0 :         break;
     918            0 :     case ClefType::TAB:         // TAB clef
     919              :     case ClefType::TAB4:        // TAB clef 4 strings
     920              :     case ClefType::TAB_SERIF:   // TAB clef alternate style
     921              :     case ClefType::TAB4_SERIF:  // TAB clef alternate style
     922            0 :         yoff = lineDist * (lines - 1) * 0.5;
     923            0 :         break;
     924            0 :     case ClefType::PERC:        // percussion clefs
     925              :     case ClefType::PERC2:
     926            0 :         yoff = lineDist * (lines - 1) * 0.5;
     927            0 :         break;
     928            0 :     default:
     929            0 :         break;
     930              :     }
     931              : 
     932            0 :     ldata->setPos(0.0, yoff.toAbsolute(spatium));
     933              : 
     934            0 :     RectF bbox = item->symBbox(ldata->symId);
     935            0 :     ldata->setBbox(bbox);
     936            0 : }
     937              : 
     938            0 : void SingleLayout::layout(Expression* item, const Context& ctx)
     939              : {
     940            0 :     layoutTextBase(item, ctx, item->mutldata());
     941            0 : }
     942              : 
     943            0 : void SingleLayout::layout(Fermata* item, const Context&)
     944              : {
     945            0 :     item->setPos(PointF());
     946            0 :     item->setOffset(PointF());
     947            0 :     RectF b(item->symBbox(item->symId()));
     948            0 :     item->setbbox(b.translated(-0.5 * b.width(), 0.0));
     949            0 : }
     950              : 
     951            0 : void SingleLayout::layout(Fingering* item, const Context& ctx)
     952              : {
     953            0 :     layoutTextBase(item, ctx, item->mutldata());
     954            0 : }
     955              : 
     956            0 : void SingleLayout::layout(FretDiagram* item, const Context& ctx)
     957              : {
     958            0 :     FretDiagram::LayoutData* ldata = item->mutldata();
     959            0 :     double spatium  = item->spatium();
     960            0 :     ldata->stringLineWidth = (spatium * 0.08);
     961            0 :     ldata->nutLineWidth = ((item->fretOffset() || !item->showNut()) ? ldata->stringLineWidth : spatium * 0.2);
     962            0 :     ldata->stringDist = (ctx.style().styleAbsolute(Sid::fretStringSpacing));
     963            0 :     ldata->fretDist = (ctx.style().styleAbsolute(Sid::fretFretSpacing));
     964            0 :     ldata->markerSize = (ldata->stringDist * 0.8);
     965              : 
     966            0 :     double w = ldata->stringDist * (item->strings() - 1) + ldata->markerSize;
     967            0 :     double h = (item->frets() + 1) * ldata->fretDist + ldata->markerSize;
     968            0 :     double y = -(ldata->markerSize * 0.5 + ldata->fretDist);
     969            0 :     double x = -(ldata->markerSize * 0.5);
     970              : 
     971              :     // Allocate space for fret offset number
     972            0 :     if (item->fretOffset() > 0) {
     973            0 :         Font scaledFont(item->fretNumFont());
     974            0 :         scaledFont.setPointSizeF(item->fretNumFont().pointSizeF() * item->userMag());
     975              : 
     976            0 :         FontMetrics fm2(scaledFont);
     977            0 :         double numw = fm2.width(String::number(item->fretOffset() + 1));
     978            0 :         double xdiff = numw + ldata->stringDist * .4;
     979            0 :         w += xdiff;
     980            0 :         x += (item->numPos() == 0) == (item->orientation() == engraving::Orientation::VERTICAL) ? -xdiff : 0;
     981            0 :     }
     982              : 
     983            0 :     if (item->orientation() == engraving::Orientation::HORIZONTAL) {
     984            0 :         std::swap(w, h);
     985            0 :         std::swap(x, y);
     986              :     }
     987              : 
     988            0 :     ldata->setBbox(x, y, w, h);
     989            0 : }
     990              : 
     991            0 : void SingleLayout::layout(FSymbol* item, const Context&)
     992              : {
     993            0 :     item->setbbox(FontMetrics::boundingRect(item->font(), item->toString()));
     994            0 :     item->setOffset(0.0, 0.0);
     995            0 :     item->setPos(0.0, 0.0);
     996            0 : }
     997              : 
     998            0 : void SingleLayout::layout(Dynamic* item, const Context& ctx)
     999              : {
    1000            0 :     layoutTextBase(item, ctx, item->mutldata());
    1001            0 : }
    1002              : 
    1003            0 : void SingleLayout::layout(Glissando* item, const Context& ctx)
    1004              : {
    1005            0 :     double spatium = item->spatium();
    1006              : 
    1007            0 :     if (item->spannerSegments().empty()) {
    1008            0 :         item->add(item->createLineSegment());
    1009              :     }
    1010            0 :     LineSegment* s = item->frontSegment();
    1011            0 :     s->setPos(PointF(-spatium * Glissando::GLISS_PALETTE_WIDTH / 2, spatium * Glissando::GLISS_PALETTE_HEIGHT / 2));
    1012            0 :     s->setPos2(PointF(spatium * Glissando::GLISS_PALETTE_WIDTH, -spatium * Glissando::GLISS_PALETTE_HEIGHT));
    1013            0 :     layout(static_cast<GlissandoSegment*>(s), ctx);
    1014            0 : }
    1015              : 
    1016            0 : void SingleLayout::layout(GlissandoSegment* item, const Context&)
    1017              : {
    1018            0 :     if (item->pos2().x() <= 0) {
    1019            0 :         item->setbbox(RectF());
    1020            0 :         return;
    1021              :     }
    1022              : 
    1023            0 :     RectF r = RectF(0.0, 0.0, item->pos2().x(), item->pos2().y()).normalized();
    1024            0 :     double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
    1025            0 :     item->setbbox(r.adjusted(-lw, -lw, lw, lw));
    1026              : }
    1027              : 
    1028            0 : void SingleLayout::layout(GradualTempoChange* item, const Context& ctx)
    1029              : {
    1030            0 :     layoutLine(item, ctx);
    1031            0 : }
    1032              : 
    1033            0 : void SingleLayout::layout(GradualTempoChangeSegment* item, const Context& ctx)
    1034              : {
    1035            0 :     layoutTextLineBaseSegment(item, ctx);
    1036            0 :     item->setOffset(PointF());
    1037            0 : }
    1038              : 
    1039            0 : void SingleLayout::layout(GuitarBend*, const Context&)
    1040              : {
    1041            0 :     NOT_IMPLEMENTED;
    1042              :     //! NOTE: Bends can be removed from disallowed elements in NotationInteraction::isOutgoingDragElementAllowed once this has been implemented
    1043            0 : }
    1044              : 
    1045            0 : void SingleLayout::layout(GuitarBendSegment*, const Context&)
    1046              : {
    1047            0 :     NOT_IMPLEMENTED;
    1048            0 : }
    1049              : 
    1050            0 : void SingleLayout::layout(Hairpin* item, const Context& ctx)
    1051              : {
    1052            0 :     item->setPos(0.0, 0.0);
    1053            0 :     layoutLine(item, ctx);
    1054            0 : }
    1055              : 
    1056            0 : void SingleLayout::layout(HammerOnPullOff* item, const Context& ctx)
    1057              : {
    1058            0 :     double spatium = item->spatium();
    1059            0 :     HammerOnPullOffSegment* s = nullptr;
    1060            0 :     if (item->spannerSegments().empty()) {
    1061            0 :         s = new HammerOnPullOffSegment(item);
    1062            0 :         s->setTrack(item->track());
    1063            0 :         item->add(s);
    1064              :     } else {
    1065            0 :         s = toHammerOnPullOffSegment(item->frontSegment());
    1066              :     }
    1067              : 
    1068            0 :     s->setSpannerSegmentType(SpannerSegmentType::SINGLE);
    1069              : 
    1070            0 :     s->setPos(PointF());
    1071            0 :     s->ups(Grip::START).p = PointF(0, 0);
    1072            0 :     s->ups(Grip::END).p   = PointF(spatium * 6, 0);
    1073            0 :     s->setExtraHeight(0.0);
    1074              : 
    1075            0 :     SlurTieLayout::computeBezier(s);
    1076              : 
    1077            0 :     layout(s, ctx);
    1078              : 
    1079            0 :     item->setbbox(s->ldata()->bbox());
    1080            0 : }
    1081              : 
    1082            0 : void SingleLayout::layout(HammerOnPullOffSegment* item, const Context& ctx)
    1083              : {
    1084            0 :     const std::vector<HammerOnPullOffText*>& hopoTexts = item->hopoText();
    1085            0 :     if (item->hopoText().empty()) {
    1086            0 :         item->addHopoText(new HammerOnPullOffText(item));
    1087              :     }
    1088              : 
    1089            0 :     HammerOnPullOffText* hopoText = hopoTexts.front();
    1090            0 :     hopoText->setXmlText("H/P");
    1091              : 
    1092            0 :     Align align;
    1093            0 :     align.vertical = AlignV::BASELINE;
    1094            0 :     align.horizontal = AlignH::HCENTER;
    1095            0 :     hopoText->setAlign(align);
    1096            0 :     hopoText->setPosition(AlignH::HCENTER);
    1097            0 :     layoutTextBase(hopoText, ctx, hopoText->mutldata());
    1098              : 
    1099            0 :     RectF bbox = item->ldata()->bbox();
    1100            0 :     double x = 0.5 * (bbox.left() + bbox.right());
    1101            0 :     double y = bbox.top() - 0.5 * item->spatium();
    1102            0 :     hopoText->mutldata()->setPos(x, y);
    1103              : 
    1104            0 :     Shape itemShape = item->mutldata()->shape();
    1105            0 :     itemShape.add(hopoText->shape().translated(hopoText->pos()));
    1106            0 :     item->mutldata()->setShape(itemShape);
    1107            0 : }
    1108              : 
    1109            0 : void SingleLayout::layout(HairpinSegment* item, const Context& ctx)
    1110              : {
    1111            0 :     const double spatium = item->spatium();
    1112            0 :     HairpinSegment::LayoutData* ldata = item->mutldata();
    1113              : 
    1114            0 :     HairpinType type = item->hairpin()->hairpinType();
    1115            0 :     if (item->hairpin()->isLineType()) {
    1116            0 :         item->setTwoLines(false);
    1117            0 :         layoutTextLineBaseSegment(item, ctx);
    1118            0 :         item->setDrawCircledTip(false);
    1119            0 :         item->setCircledTipRadius(0.0);
    1120              :     } else {
    1121            0 :         item->setTwoLines(true);
    1122              : 
    1123            0 :         item->hairpin()->setBeginTextAlign({ AlignH::LEFT, AlignV::VCENTER });
    1124            0 :         item->hairpin()->setEndTextAlign({ AlignH::RIGHT, AlignV::VCENTER });
    1125              : 
    1126            0 :         double x1 = 0.0;
    1127            0 :         layoutTextLineBaseSegment(item, ctx);
    1128            0 :         if (!item->text()->empty()) {
    1129            0 :             x1 = item->text()->width() + spatium * .5;
    1130              :         }
    1131              : 
    1132            0 :         Transform t;
    1133            0 :         double h1 = item->hairpin()->hairpinHeight().val() * spatium * .5;
    1134            0 :         double h2 = item->hairpin()->hairpinContHeight().val() * spatium * .5;
    1135              : 
    1136            0 :         double x = item->pos2().x();
    1137            0 :         if (!item->endText()->empty()) {
    1138            0 :             x -= (item->endText()->width() + spatium * .5);             // 0.5 spatium distance
    1139              :         }
    1140            0 :         if (x < spatium) {               // minimum size of hairpin
    1141            0 :             x = spatium;
    1142              :         }
    1143            0 :         double y = item->pos2().y();
    1144            0 :         double len = sqrt(x * x + y * y);
    1145            0 :         t.rotateRadians(asin(y / len));
    1146              : 
    1147            0 :         item->setDrawCircledTip(item->hairpin()->hairpinCircledTip());
    1148            0 :         item->setCircledTipRadius(item->drawCircledTip() ? 0.6 * spatium * .5 : 0.0);
    1149              : 
    1150            0 :         LineF l1, l2;
    1151              : 
    1152            0 :         switch (type) {
    1153            0 :         case HairpinType::CRESC_HAIRPIN: {
    1154            0 :             switch (item->spannerSegmentType()) {
    1155            0 :             case SpannerSegmentType::SINGLE:
    1156              :             case SpannerSegmentType::BEGIN: {
    1157            0 :                 l1.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, h1);
    1158            0 :                 l2.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, -h1);
    1159            0 :                 PointF circledTip;
    1160            0 :                 circledTip.setX(x1 + item->circledTipRadius());
    1161            0 :                 circledTip.setY(0.0);
    1162            0 :                 item->setCircledTip(circledTip);
    1163            0 :             } break;
    1164              : 
    1165            0 :             case SpannerSegmentType::MIDDLE:
    1166              :             case SpannerSegmentType::END:
    1167            0 :                 item->setDrawCircledTip(false);
    1168            0 :                 l1.setLine(x1,  h2, len, h1);
    1169            0 :                 l2.setLine(x1, -h2, len, -h1);
    1170            0 :                 break;
    1171              :             }
    1172              :         }
    1173            0 :         break;
    1174            0 :         case HairpinType::DIM_HAIRPIN: {
    1175            0 :             switch (item->spannerSegmentType()) {
    1176            0 :             case SpannerSegmentType::SINGLE:
    1177              :             case SpannerSegmentType::END: {
    1178            0 :                 l1.setLine(x1,  h1, len - item->circledTipRadius() * 2, 0.0);
    1179            0 :                 l2.setLine(x1, -h1, len - item->circledTipRadius() * 2, 0.0);
    1180            0 :                 PointF circledTip;
    1181            0 :                 circledTip.setX(len - item->circledTipRadius());
    1182            0 :                 circledTip.setY(0.0);
    1183            0 :                 item->setCircledTip(circledTip);
    1184            0 :             } break;
    1185            0 :             case SpannerSegmentType::BEGIN:
    1186              :             case SpannerSegmentType::MIDDLE:
    1187            0 :                 item->setDrawCircledTip(false);
    1188            0 :                 l1.setLine(x1,  h1, len, +h2);
    1189            0 :                 l2.setLine(x1, -h1, len, -h2);
    1190            0 :                 break;
    1191              :             }
    1192              :         }
    1193            0 :         break;
    1194            0 :         default:
    1195            0 :             break;
    1196              :         }
    1197              : 
    1198              :         // Do Coord rotation
    1199            0 :         l1 = t.map(l1);
    1200            0 :         l2 = t.map(l2);
    1201            0 :         if (item->drawCircledTip()) {
    1202            0 :             item->setCircledTip(t.map(item->circledTip()));
    1203              :         }
    1204              : 
    1205            0 :         ldata->points[0] = l1.p1();
    1206            0 :         ldata->points[1] = l1.p2();
    1207            0 :         ldata->points[2] = l2.p1();
    1208            0 :         ldata->points[3] = l2.p2();
    1209            0 :         ldata->npoints   = 4;
    1210              : 
    1211            0 :         RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
    1212            0 :         if (!item->text()->empty()) {
    1213            0 :             r.unite(item->text()->ldata()->bbox());
    1214              :         }
    1215            0 :         if (!item->endText()->empty()) {
    1216            0 :             r.unite(item->endText()->ldata()->bbox().translated(x + item->endText()->ldata()->bbox().width(), 0.0));
    1217              :         }
    1218            0 :         double w = item->absoluteFromSpatium(ctx.style().styleS(Sid::hairpinLineWidth));
    1219            0 :         item->setbbox(r.adjusted(-w * .5, -w * .5, w, w));
    1220              :     }
    1221              : 
    1222            0 :     item->setPos(PointF());
    1223            0 :     item->setOffset(PointF());
    1224            0 : }
    1225              : 
    1226            0 : void SingleLayout::layout(HarpPedalDiagram* item, const Context& ctx)
    1227              : {
    1228            0 :     item->updateDiagramText();
    1229            0 :     layoutTextBase(item, ctx, item->mutldata());
    1230            0 : }
    1231              : 
    1232            0 : void SingleLayout::layout(Image* item, const Context&)
    1233              : {
    1234            0 :     item->init();
    1235              : 
    1236            0 :     SizeF imageSize = item->size();
    1237              : 
    1238            0 :     Image::LayoutData* ldata = item->mutldata();
    1239            0 :     ldata->setPos(PointF());
    1240            0 :     ldata->setBbox(RectF(PointF(), item->size2pixel(imageSize)));
    1241            0 : }
    1242              : 
    1243            0 : void SingleLayout::layout(InstrumentChange* item, const Context& ctx)
    1244              : {
    1245            0 :     layoutTextBase(item, ctx, item->mutldata());
    1246            0 : }
    1247              : 
    1248            0 : void SingleLayout::layout(Jump* item, const Context& ctx)
    1249              : {
    1250            0 :     layoutTextBase(item, ctx, item->mutldata());
    1251            0 : }
    1252              : 
    1253            0 : void SingleLayout::layout(KeySig* item, const Context& ctx)
    1254              : {
    1255            0 :     KeySig::LayoutData* ldata = item->mutldata();
    1256            0 :     double spatium = item->spatium();
    1257            0 :     double step = spatium * 0.5;
    1258              : 
    1259            0 :     ldata->setBbox(RectF());
    1260            0 :     ldata->keySymbols.clear();
    1261              : 
    1262              :     // determine current clef for this staff
    1263            0 :     ClefType clef = ClefType::G;
    1264              : 
    1265            0 :     int key = int(item->key());
    1266              : 
    1267            0 :     if (item->isCustom() && !item->isAtonal()) {
    1268            0 :         Spatium accidentalGap = ctx.style().styleS(Sid::keysigAccidentalDistance);
    1269              :         // add standard key accidentals first, if necessary
    1270            0 :         for (int i = 1; i <= std::abs(key) && std::abs(key) <= 7; ++i) {
    1271            0 :             bool drop = false;
    1272            0 :             for (const CustDef& cd: item->customKeyDefs()) {
    1273            0 :                 int degree = item->degInKey(cd.degree);
    1274              :                 // if custom keysig accidental takes place, don't create tonal accidental
    1275            0 :                 if ((degree * 2 + 2) % 7 == (key < 0 ? 8 - i : i) % 7) {
    1276            0 :                     drop = true;
    1277            0 :                     break;
    1278              :                 }
    1279              :             }
    1280            0 :             if (!drop) {
    1281            0 :                 KeySym ks;
    1282            0 :                 int lineIndexOffset = key > 0 ? -1 : 6;
    1283            0 :                 ks.sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    1284            0 :                 ks.line = ClefInfo::lines(clef)[lineIndexOffset + i];
    1285            0 :                 if (ldata->keySymbols.size() > 0) {
    1286            0 :                     KeySym& previous = ldata->keySymbols.back();
    1287            0 :                     Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
    1288            0 :                     ks.xPos = previous.xPos + previousWidth + accidentalGap;
    1289              :                 } else {
    1290            0 :                     ks.xPos = 0_sp;
    1291              :                 }
    1292              :                 // TODO octave metters?
    1293            0 :                 ldata->keySymbols.push_back(ks);
    1294              :             }
    1295              :         }
    1296            0 :         for (const CustDef& cd : item->customKeyDefs()) {
    1297            0 :             SymId sym = item->symInKey(cd.sym, cd.degree);
    1298            0 :             int degree = item->degInKey(cd.degree);
    1299            0 :             bool flat = std::string(SymNames::nameForSymId(sym).ascii()).find("Flat") != std::string::npos;
    1300            0 :             int accIdx = (degree * 2 + 1) % 7; // C D E F ... index to F C G D index
    1301            0 :             accIdx = flat ? 13 - accIdx : accIdx;
    1302            0 :             int line = ClefInfo::lines(clef)[accIdx] + cd.octAlt * 7;
    1303            0 :             Spatium xpos = cd.xAlt;
    1304            0 :             if (ldata->keySymbols.size() > 0) {
    1305            0 :                 KeySym& previous = ldata->keySymbols.back();
    1306            0 :                 Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
    1307            0 :                 xpos += previous.xPos + previousWidth + accidentalGap;
    1308              :             }
    1309              :             // if translated symbol if out of range, add key accidental followed by untranslated symbol
    1310            0 :             if (sym == SymId::noSym) {
    1311            0 :                 KeySym ks;
    1312            0 :                 ks.line = line;
    1313            0 :                 ks.xPos = xpos;
    1314              :                 // for quadruple sharp use two double sharps
    1315            0 :                 if (cd.sym == SymId::accidentalTripleSharp) {
    1316            0 :                     ks.sym = SymId::accidentalDoubleSharp;
    1317            0 :                     sym = SymId::accidentalDoubleSharp;
    1318              :                 } else {
    1319            0 :                     ks.sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    1320            0 :                     sym = cd.sym;
    1321              :                 }
    1322            0 :                 ldata->keySymbols.push_back(ks);
    1323            0 :                 xpos += Spatium(key < 0 ? 0.7 : 1); // flats closer
    1324              :             }
    1325              :             // create symbol; natural only if is user defined
    1326            0 :             if (sym != SymId::accidentalNatural || sym == cd.sym) {
    1327            0 :                 KeySym ks;
    1328            0 :                 ks.sym = sym;
    1329            0 :                 ks.line = line;
    1330            0 :                 ks.xPos = xpos;
    1331            0 :                 ldata->keySymbols.push_back(ks);
    1332              :             }
    1333              :         }
    1334              :     } else {
    1335            0 :         if (std::abs(key) <= 7) {
    1336            0 :             const signed char* lines = ClefInfo::lines(clef);
    1337            0 :             SymId sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    1338            0 :             Spatium accidentalGap = ctx.style().styleS(Sid::keysigAccidentalDistance);
    1339            0 :             Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(sym), spatium);
    1340            0 :             int lineIndexOffset = key > 0 ? 0 : 7;
    1341            0 :             for (int i = 0; i < std::abs(key); ++i) {
    1342            0 :                 int line = lines[lineIndexOffset + i];
    1343            0 :                 KeySym ks;
    1344            0 :                 ks.sym = sym;
    1345            0 :                 Spatium x = 0.0_sp;
    1346            0 :                 if (ldata->keySymbols.size() > 0) {
    1347            0 :                     const KeySym& previous = ldata->keySymbols.back();
    1348            0 :                     x = previous.xPos + previousWidth + accidentalGap;
    1349            0 :                     bool isAscending = line < previous.line;
    1350            0 :                     SmuflAnchorId currentCutout = isAscending ? SmuflAnchorId::cutOutSW : SmuflAnchorId::cutOutNW;
    1351            0 :                     SmuflAnchorId previousCutout = isAscending ? SmuflAnchorId::cutOutNE : SmuflAnchorId::cutOutSE;
    1352            0 :                     PointF cutout = item->symSmuflAnchor(sym, currentCutout);
    1353            0 :                     double currentCutoutY = line * step + cutout.y();
    1354            0 :                     double previousCutoutY = previous.line * step + item->symSmuflAnchor(previous.sym, previousCutout).y();
    1355            0 :                     if ((isAscending && currentCutoutY < previousCutoutY) || (!isAscending && currentCutoutY > previousCutoutY)) {
    1356            0 :                         x -= Spatium::fromAbsolute(cutout.x(), spatium);
    1357              :                     }
    1358              :                 }
    1359            0 :                 ks.xPos = x;
    1360            0 :                 ks.line = line;
    1361            0 :                 ldata->keySymbols.push_back(ks);
    1362              :             }
    1363              :         } else {
    1364            0 :             LOGD() << "illegal key:" << key;
    1365              :         }
    1366              :     }
    1367              : 
    1368              :     // compute bbox
    1369            0 :     for (const KeySym& ks : ldata->keySymbols) {
    1370            0 :         double x = ks.xPos.toAbsolute(spatium);
    1371            0 :         double y = ks.line * step;
    1372            0 :         ldata->addBbox(item->symBbox(ks.sym).translated(x, y));
    1373              :     }
    1374            0 : }
    1375              : 
    1376            0 : void SingleLayout::layout(LayoutBreak* item, const Context&)
    1377              : {
    1378            0 :     FontMetrics metrics(item->font());
    1379            0 :     RectF bbox = metrics.boundingRect(item->iconCode());
    1380            0 :     item->mutldata()->setShape(Shape(bbox, item));
    1381            0 : }
    1382              : 
    1383            0 : void SingleLayout::layout(LetRing* item, const Context& ctx)
    1384              : {
    1385            0 :     layoutLine(item, ctx);
    1386            0 : }
    1387              : 
    1388            0 : void SingleLayout::layout(LetRingSegment* item, const Context& ctx)
    1389              : {
    1390            0 :     layoutTextLineBaseSegment(item, ctx);
    1391            0 : }
    1392              : 
    1393            0 : void SingleLayout::layout(Lyrics* item, const Context& ctx)
    1394              : {
    1395            0 :     layoutTextBase(toTextBase(item), ctx, item->mutldata());
    1396            0 : }
    1397              : 
    1398            0 : void SingleLayout::layout(NoteHead* item, const Context& ctx)
    1399              : {
    1400            0 :     layout(static_cast<Symbol*>(item), ctx);
    1401            0 : }
    1402              : 
    1403            0 : void SingleLayout::layout(NoteLine*, const Context&)
    1404              : {
    1405            0 :     NOT_IMPLEMENTED;
    1406              :     //! NOTE: NoteLines can be removed from disallowed elements in NotationInteraction::isOutgoingDragElementAllowed once this has been implemented
    1407            0 : }
    1408              : 
    1409            0 : void SingleLayout::layout(Marker* item, const Context& ctx)
    1410              : {
    1411            0 :     layoutTextBase(item, ctx, item->mutldata());
    1412            0 : }
    1413              : 
    1414            0 : void SingleLayout::layout(MeasureNumber* item, const Context& ctx)
    1415              : {
    1416            0 :     item->setPos(PointF());
    1417            0 :     item->setOffset(PointF());
    1418              : 
    1419            0 :     layout1TextBase(item, ctx, item->mutldata());
    1420            0 : }
    1421              : 
    1422            0 : void SingleLayout::layout(MeasureRepeat* item, const Context& ctx)
    1423              : {
    1424            0 :     MeasureRepeat::LayoutData* ldata = item->mutldata();
    1425            0 :     switch (item->numMeasures()) {
    1426            0 :     case 1:
    1427              :     {
    1428            0 :         ldata->setSymId(SymId::repeat1Bar);
    1429            0 :         if (ctx.style().styleB(Sid::oneMeasureRepeatShow1)) {
    1430            0 :             ldata->setNumberSym(1);
    1431              :         } else {
    1432            0 :             ldata->clearNumberSym();
    1433              :         }
    1434            0 :         break;
    1435              :     }
    1436            0 :     case 2:
    1437            0 :         ldata->setSymId(SymId::repeat2Bars);
    1438            0 :         ldata->setNumberSym(item->numMeasures());
    1439            0 :         break;
    1440            0 :     case 4:
    1441            0 :         ldata->setSymId(SymId::repeat4Bars);
    1442            0 :         ldata->setNumberSym(item->numMeasures());
    1443            0 :         break;
    1444            0 :     default:
    1445            0 :         ldata->setSymId(SymId::noSym); // should never happen
    1446            0 :         ldata->clearNumberSym();
    1447            0 :         break;
    1448              :     }
    1449              : 
    1450            0 :     RectF bbox = item->symBbox(ldata->symId);
    1451            0 :     ldata->setBbox(bbox);
    1452            0 : }
    1453              : 
    1454            0 : void SingleLayout::layout(Ornament* item, const Context& ctx)
    1455              : {
    1456            0 :     layout(toArticulation(item), ctx);
    1457            0 : }
    1458              : 
    1459            0 : void SingleLayout::layout(Ottava* item, const Context& ctx)
    1460              : {
    1461            0 :     layoutLine(item, ctx);
    1462            0 : }
    1463              : 
    1464            0 : void SingleLayout::layout(OttavaSegment* item, const Context& ctx)
    1465              : {
    1466            0 :     layoutTextLineBaseSegment(item, ctx);
    1467            0 :     item->setOffset(PointF());
    1468            0 : }
    1469              : 
    1470            0 : void SingleLayout::layout(PalmMute* item, const Context& ctx)
    1471              : {
    1472            0 :     layoutLine(item, ctx);
    1473            0 : }
    1474              : 
    1475            0 : void SingleLayout::layout(PalmMuteSegment* item, const Context& ctx)
    1476              : {
    1477            0 :     layoutTextLineBaseSegment(item, ctx);
    1478            0 : }
    1479              : 
    1480            0 : void SingleLayout::layout(Pedal* item, const Context& ctx)
    1481              : {
    1482            0 :     layoutLine(item, ctx);
    1483            0 : }
    1484              : 
    1485            0 : void SingleLayout::layout(PedalSegment* item, const Context& ctx)
    1486              : {
    1487            0 :     layoutTextLineBaseSegment(item, ctx);
    1488            0 :     item->setOffset(PointF());
    1489            0 : }
    1490              : 
    1491            0 : void SingleLayout::layout(PlayCountText* item, const Context& ctx)
    1492              : {
    1493            0 :     layoutTextBase(item, ctx, item->mutldata());
    1494            0 : }
    1495              : 
    1496            0 : void SingleLayout::layout(PlayTechAnnotation* item, const Context& ctx)
    1497              : {
    1498            0 :     layoutTextBase(item, ctx, item->mutldata());
    1499            0 : }
    1500              : 
    1501            0 : void SingleLayout::layout(RehearsalMark* item, const Context& ctx)
    1502              : {
    1503            0 :     layoutTextBase(item, ctx, item->mutldata());
    1504            0 : }
    1505              : 
    1506            0 : void SingleLayout::layout(Slur* item, const Context&)
    1507              : {
    1508            0 :     double spatium = item->spatium();
    1509            0 :     SlurSegment* s = nullptr;
    1510            0 :     if (item->spannerSegments().empty()) {
    1511            0 :         s = new SlurSegment(item);
    1512            0 :         s->setTrack(item->track());
    1513            0 :         item->add(s);
    1514              :     } else {
    1515            0 :         s = item->frontSegment();
    1516              :     }
    1517              : 
    1518            0 :     s->setSpannerSegmentType(SpannerSegmentType::SINGLE);
    1519              : 
    1520            0 :     s->setPos(PointF());
    1521            0 :     s->ups(Grip::START).p = PointF(0, 0);
    1522            0 :     s->ups(Grip::END).p   = PointF(spatium * 6, 0);
    1523            0 :     s->setExtraHeight(0.0);
    1524              : 
    1525            0 :     SlurTieLayout::computeBezier(s);
    1526              : 
    1527            0 :     item->setbbox(s->ldata()->bbox());
    1528            0 : }
    1529              : 
    1530            0 : void SingleLayout::layout(Spacer* item, const Context&)
    1531              : {
    1532            0 :     Spacer::LayoutData* ldata = item->mutldata();
    1533              : 
    1534            0 :     double spatium = item->spatium();
    1535              : 
    1536            0 :     PainterPath path = PainterPath();
    1537            0 :     double w = spatium;
    1538            0 :     double b = w * .5;
    1539            0 :     double h = item->ownershipParent() ? item->absoluteGap() : item->absoluteFromSpatium(std::min(item->gap(), 4.0_sp));       // limit length for palette
    1540              : 
    1541            0 :     switch (item->spacerType()) {
    1542            0 :     case SpacerType::DOWN:
    1543            0 :         path.lineTo(w, 0.0);
    1544            0 :         path.moveTo(b, 0.0);
    1545            0 :         path.lineTo(b, h);
    1546            0 :         path.lineTo(0.0, h - b);
    1547            0 :         path.moveTo(b, h);
    1548            0 :         path.lineTo(w, h - b);
    1549            0 :         break;
    1550            0 :     case SpacerType::UP:
    1551            0 :         path.moveTo(b, 0.0);
    1552            0 :         path.lineTo(0.0, b);
    1553            0 :         path.moveTo(b, 0.0);
    1554            0 :         path.lineTo(w, b);
    1555            0 :         path.moveTo(b, 0.0);
    1556            0 :         path.lineTo(b, h);
    1557            0 :         path.moveTo(0.0, h);
    1558            0 :         path.lineTo(w, h);
    1559            0 :         break;
    1560            0 :     case SpacerType::FIXED:
    1561            0 :         path.lineTo(w, 0.0);
    1562            0 :         path.moveTo(b, 0.0);
    1563            0 :         path.lineTo(b, h);
    1564            0 :         path.moveTo(0.0, h);
    1565            0 :         path.lineTo(w, h);
    1566            0 :         break;
    1567              :     }
    1568            0 :     ldata->path = path;
    1569            0 :     double lw = spatium * 0.4;
    1570            0 :     RectF bb(0, 0, w, h);
    1571            0 :     bb.adjust(-lw, -lw, lw, lw);
    1572            0 :     ldata->setBbox(bb);
    1573              : 
    1574            0 :     item->setZ(0.0);
    1575            0 : }
    1576              : 
    1577            0 : void SingleLayout::layout(StaffText* item, const Context& ctx)
    1578              : {
    1579            0 :     layoutTextBase(item, ctx, item->mutldata());
    1580              : 
    1581            0 :     if (item->hasSoundFlag()) {
    1582            0 :         RectF bbox = item->ldata()->bbox();
    1583            0 :         double iconHeight = bbox.height();
    1584            0 :         RectF iconBBox = RectF(bbox.x(), bbox.y(), iconHeight, iconHeight);
    1585            0 :         item->soundFlag()->mutldata()->setBbox(iconBBox);
    1586              : 
    1587            0 :         double xMove = iconBBox.width() + iconBBox.width() / 2.0;
    1588            0 :         bbox.setWidth(bbox.width() + xMove);
    1589            0 :         item->setbbox(bbox);
    1590              : 
    1591            0 :         layout(item->soundFlag(), ctx);
    1592              : 
    1593            0 :         for (TextBlock& block : item->mutldata()->blocks) {
    1594            0 :             auto& fragments = block.fragments();
    1595            0 :             for (std::list<TextFragment>::iterator it = fragments.begin(); it != fragments.end(); ++it) {
    1596            0 :                 it->pos.setX(it->pos.x() + xMove);
    1597              :             }
    1598              :         }
    1599              :     }
    1600            0 : }
    1601              : 
    1602            0 : void SingleLayout::layout(StaveSharingLabel* item, const Context& ctx)
    1603              : {
    1604            0 :     layoutTextBase(item, ctx, item->mutldata());
    1605            0 : }
    1606              : 
    1607            0 : void SingleLayout::layout(StaffTypeChange* item, const Context& ctx)
    1608              : {
    1609            0 :     double spatium = ctx.style().spatium();
    1610            0 :     item->setbbox(RectF(-item->lw() * .5, -item->lw() * .5, spatium * 2.5 + item->lw(), spatium * 2.5 + item->lw()));
    1611            0 :     item->setPos(0.0, 0.0);
    1612            0 : }
    1613              : 
    1614            0 : void SingleLayout::layout(StringTunings* item, const Context& ctx)
    1615              : {
    1616            0 :     layoutTextBase(item, ctx, item->mutldata());
    1617              : 
    1618            0 :     double spatium = item->spatium();
    1619            0 :     for (TextBlock& block : item->mutldata()->blocks) {
    1620            0 :         for (TextFragment& fragment : block.fragments()) {
    1621            0 :             Font font = fragment.font(item);
    1622            0 :             if (font.type() != Font::Type::MusicSymbol) {
    1623              :                 // HACK: the music symbol doesn't have a good baseline
    1624              :                 // to go with text so we correct text here
    1625            0 :                 const double baselineAdjustment = 0.35 * spatium * item->symbolScale();
    1626            0 :                 fragment.pos.setY(fragment.pos.y() - baselineAdjustment);
    1627              :             }
    1628            0 :         }
    1629              :     }
    1630            0 : }
    1631              : 
    1632            0 : void SingleLayout::layout(Symbol* item, const Context&)
    1633              : {
    1634            0 :     item->setbbox(item->scoreFont() ? item->scoreFont()->bbox(item->sym(), item->magS()) : item->symBbox(item->sym()));
    1635            0 :     item->setOffset(0.0, 0.0);
    1636            0 :     item->setPos(0.0, 0.0);
    1637            0 : }
    1638              : 
    1639            0 : void SingleLayout::layout(SystemText* item, const Context& ctx)
    1640              : {
    1641            0 :     layoutTextBase(item, ctx, item->mutldata());
    1642            0 : }
    1643              : 
    1644            0 : void SingleLayout::layout(Tapping* item, const Context& ctx)
    1645              : {
    1646            0 :     TappingText* text = item->text();
    1647              : 
    1648            0 :     if (!text) {
    1649            0 :         text = new TappingText(item);
    1650              :     }
    1651              : 
    1652            0 :     item->setText(text);
    1653            0 :     text->setTrack(item->track());
    1654            0 :     DO_ASSERT(item->hand() != TappingHand::INVALID);
    1655            0 :     text->setXmlText(item->hand() == TappingHand::LEFT ? "l.h. tap" : "r.h. tap");
    1656            0 :     text->setAlign(Align(AlignH::HCENTER, AlignV::BASELINE));
    1657              : 
    1658            0 :     layoutTextBase(text, ctx, text->mutldata());
    1659              : 
    1660            0 :     item->setbbox(text->ldata()->bbox());
    1661            0 : }
    1662              : 
    1663            0 : void SingleLayout::layout(SoundFlag* item, const Context& ctx)
    1664              : {
    1665              :     UNUSED(item);
    1666              :     UNUSED(ctx);
    1667            0 : }
    1668              : 
    1669            0 : void SingleLayout::layout(Stem* item, const Context& ctx)
    1670              : {
    1671            0 :     LayoutContext tctx(ctx.dontUseScore());
    1672            0 :     TLayout::layoutStem(item, item->mutldata(), tctx.conf());
    1673            0 : }
    1674              : 
    1675            0 : void SingleLayout::layout(Sticking* item, const Context& ctx)
    1676              : {
    1677            0 :     layoutTextBase(toTextBase(item), ctx, item->mutldata());
    1678            0 : }
    1679              : 
    1680            0 : void SingleLayout::layout(TempoText* item, const Context& ctx)
    1681              : {
    1682            0 :     layoutTextBase(item, ctx, item->mutldata());
    1683            0 : }
    1684              : 
    1685            0 : void SingleLayout::layout(TextLine* item, const Context& ctx)
    1686              : {
    1687            0 :     layoutLine(item, ctx);
    1688            0 : }
    1689              : 
    1690            0 : void SingleLayout::layout(TextLineSegment* item, const Context& ctx)
    1691              : {
    1692            0 :     layoutTextLineBaseSegment(item, ctx);
    1693            0 :     item->setOffset(PointF());
    1694            0 : }
    1695              : 
    1696            0 : void SingleLayout::layout(TimeSig* item, const Context& ctx)
    1697              : {
    1698            0 :     TimeSig::LayoutData* ldata = item->mutldata();
    1699              : 
    1700            0 :     double spatium = item->spatium();
    1701              : 
    1702            0 :     ldata->setPos(0.0, 0.0);
    1703            0 :     ldata->setBbox(RectF());                    // prepare for an empty time signature
    1704              : 
    1705            0 :     constexpr double lineDist = 1.0;
    1706            0 :     constexpr int numOfLines = 5;
    1707            0 :     TimeSigType sigType = item->timeSigType();
    1708              : 
    1709              :     // if some symbol
    1710              :     // compute vert. displacement to center in the staff height
    1711              :     // determine middle staff position:
    1712              : 
    1713            0 :     double yoff = spatium * (numOfLines - 1) * 0.5 * lineDist;
    1714              : 
    1715              :     // C and Ccut are placed at the middle of the staff: use yoff directly
    1716            0 :     IEngravingFontPtr font = ctx.engravingFont();
    1717            0 :     SizeF mag(item->magS() * item->scale());
    1718              : 
    1719            0 :     if (sigType == TimeSigType::FOUR_FOUR) {
    1720            0 :         ldata->pz = PointF(0.0, yoff);
    1721            0 :         RectF bbox = font->bbox(SymId::timeSigCommon, mag);
    1722            0 :         ldata->setBbox(bbox.translated(ldata->pz));
    1723            0 :         ldata->ns.clear();
    1724            0 :         ldata->ns.push_back(SymId::timeSigCommon);
    1725            0 :         ldata->ds.clear();
    1726            0 :     } else if (sigType == TimeSigType::ALLA_BREVE) {
    1727            0 :         ldata->pz = PointF(0.0, yoff);
    1728            0 :         RectF bbox = font->bbox(SymId::timeSigCutCommon, mag);
    1729            0 :         ldata->setBbox(bbox.translated(ldata->pz));
    1730            0 :         ldata->ns.clear();
    1731            0 :         ldata->ns.push_back(SymId::timeSigCutCommon);
    1732            0 :         ldata->ds.clear();
    1733            0 :     } else if (sigType == TimeSigType::CUT_BACH) {
    1734            0 :         ldata->pz = PointF(0.0, yoff);
    1735            0 :         RectF bbox = font->bbox(SymId::timeSigCut2, mag);
    1736            0 :         ldata->setBbox(bbox.translated(ldata->pz));
    1737            0 :         ldata->ns.clear();
    1738            0 :         ldata->ns.push_back(SymId::timeSigCut2);
    1739            0 :         ldata->ds.clear();
    1740            0 :     } else if (sigType == TimeSigType::CUT_TRIPLE) {
    1741            0 :         ldata->pz = PointF(0.0, yoff);
    1742            0 :         RectF bbox = font->bbox(SymId::timeSigCut3, mag);
    1743            0 :         ldata->setBbox(bbox.translated(ldata->pz));
    1744            0 :         ldata->ns.clear();
    1745            0 :         ldata->ns.push_back(SymId::timeSigCut3);
    1746            0 :         ldata->ds.clear();
    1747              :     } else {
    1748            0 :         if (item->numeratorString().isEmpty()) {
    1749            0 :             ldata->ns = timeSigSymIdsFromString(item->numeratorString().isEmpty()
    1750            0 :                                                 ? String::number(item->sig().numerator())
    1751            0 :                                                 : item->numeratorString());
    1752              : 
    1753            0 :             ldata->ds = timeSigSymIdsFromString(item->denominatorString().isEmpty()
    1754            0 :                                                 ? String::number(item->sig().denominator())
    1755            0 :                                                 : item->denominatorString());
    1756              :         } else {
    1757            0 :             ldata->ns = timeSigSymIdsFromString(item->numeratorString());
    1758            0 :             ldata->ds = timeSigSymIdsFromString(item->denominatorString());
    1759              :         }
    1760              : 
    1761            0 :         RectF numRect = font->bbox(ldata->ns, mag);
    1762            0 :         RectF denRect = font->bbox(ldata->ds, mag);
    1763              : 
    1764              :         // position numerator and denominator; vertical displacement:
    1765              :         // number of lines is odd: 0.0 (strings are directly above and below the middle line)
    1766              :         // number of lines even:   0.05 (strings are moved up/down to leave 1/10sp between them)
    1767              : 
    1768            0 :         double displ = (numOfLines & 1) ? 0.0 : (0.05 * spatium);
    1769              : 
    1770              :         //align on the wider
    1771            0 :         double pzY = yoff - (denRect.width() < 0.01 ? 0.0 : (displ + numRect.height() * 0.5));
    1772            0 :         double pnY = yoff + displ + denRect.height() * 0.5;
    1773              : 
    1774            0 :         if (numRect.width() >= denRect.width()) {
    1775              :             // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
    1776            0 :             ldata->pz = PointF(0.0, pzY);
    1777              :             // denominator: horiz: centred around centre of numerator | vert: one space below centre line
    1778            0 :             ldata->pn = PointF((numRect.width() - denRect.width()) * 0.5, pnY);
    1779              :         } else {
    1780              :             // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
    1781            0 :             ldata->pz = PointF((denRect.width() - numRect.width()) * 0.5, pzY);
    1782              :             // denominator: horiz: centred around centre of numerator | vert: one space below centre line
    1783            0 :             ldata->pn = PointF(0.0, pnY);
    1784              :         }
    1785              : 
    1786              :         // centering of parenthesis so the middle of the parenthesis is at the divisor marking level
    1787            0 :         int centerY = yoff / 2 + spatium;
    1788            0 :         int widestPortion = numRect.width() > denRect.width() ? numRect.width() : denRect.width();
    1789            0 :         ldata->pointLargeLeftParen = PointF(-spatium, centerY);
    1790            0 :         ldata->pointLargeRightParen = PointF(widestPortion + spatium, centerY);
    1791              : 
    1792            0 :         ldata->setBbox(numRect.translated(ldata->pz));       // translate bounding boxes to actual string positions
    1793            0 :         ldata->addBbox(denRect.translated(ldata->pn));
    1794            0 :         if (item->largeParentheses()) {
    1795            0 :             ldata->addBbox(RectF(ldata->pointLargeLeftParen.x(), ldata->pointLargeLeftParen.y() - denRect.height(), spatium / 2,
    1796            0 :                                  numRect.height() + denRect.height()));
    1797            0 :             ldata->addBbox(RectF(ldata->pointLargeRightParen.x(), ldata->pointLargeRightParen.y() - denRect.height(),  spatium / 2,
    1798            0 :                                  numRect.height() + denRect.height()));
    1799              :         }
    1800              :     }
    1801            0 : }
    1802              : 
    1803            0 : void SingleLayout::layout(TremoloSingleChord* item, const Context&)
    1804              : {
    1805            0 :     item->computeShape();      // set bbox
    1806            0 :     item->setPath(item->basePath());
    1807            0 : }
    1808              : 
    1809            0 : void SingleLayout::layout(TremoloTwoChord* item, const Context&)
    1810              : {
    1811            0 :     item->computeShape();      // set bbox
    1812            0 :     item->setPath(item->basePath());
    1813            0 : }
    1814              : 
    1815            0 : void SingleLayout::layout(TremoloBar* item, const Context&)
    1816              : {
    1817            0 :     TremoloBar::LayoutData* ldata = item->mutldata();
    1818            0 :     double spatium = item->spatium();
    1819              : 
    1820            0 :     ldata->setPos(PointF());
    1821              : 
    1822            0 :     double timeFactor  = item->userMag() / 1.0;
    1823            0 :     double pitchFactor = -spatium * 0.02;
    1824              : 
    1825            0 :     PolygonF polygon;
    1826            0 :     for (const PitchValue& v : item->points()) {
    1827            0 :         polygon << PointF(v.time * timeFactor, v.pitch * pitchFactor);
    1828              :     }
    1829            0 :     ldata->polygon = polygon;
    1830              : 
    1831            0 :     const double w = item->absoluteFromSpatium(item->lineWidth());
    1832            0 :     ldata->setBbox(ldata->polygon.boundingRect().adjusted(-w, -w, w, w));
    1833            0 : }
    1834              : 
    1835            0 : void SingleLayout::layout(Trill* item, const Context& ctx)
    1836              : {
    1837            0 :     layoutLine(static_cast<SLine*>(item), ctx);
    1838            0 : }
    1839              : 
    1840            0 : void SingleLayout::layout(TrillSegment* item, const Context& ctx)
    1841              : {
    1842            0 :     TrillSegment::LayoutData* ldata = item->mutldata();
    1843            0 :     if (item->spanner()->placeBelow()) {
    1844            0 :         ldata->setPosY(0.0);
    1845              :     }
    1846              : 
    1847            0 :     bool accidentalGoesBelow = item->trill()->trillType() == TrillType::DOWNPRALL_LINE;
    1848            0 :     Trill* trill = item->trill();
    1849            0 :     Ornament* ornament = trill->ornament();
    1850            0 :     if (ornament) {
    1851            0 :         if (item->isSingleBeginType()) {
    1852            0 :             layout(ornament, ctx);
    1853              :         }
    1854            0 :         trill->setAccidental(accidentalGoesBelow ? ornament->accidentalBelow() : ornament->accidentalAbove());
    1855            0 :         trill->setCueNoteChord(ornament->cueNoteChord());
    1856            0 :         ArticulationAnchor anchor = ornament->anchor();
    1857            0 :         if (anchor == ArticulationAnchor::AUTO) {
    1858            0 :             trill->setPlacement(trill->track() % 2 ? PlacementV::BELOW : PlacementV::ABOVE);
    1859              :         } else {
    1860            0 :             trill->setPlacement(anchor == ArticulationAnchor::TOP ? PlacementV::ABOVE : PlacementV::BELOW);
    1861              :         }
    1862            0 :         trill->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::STYLED); // Ensures that the property isn't written (it is written by the ornamnent)
    1863              :     }
    1864              : 
    1865            0 :     if (item->isSingleType() || item->isBeginType()) {
    1866            0 :         switch (item->trill()->trillType()) {
    1867            0 :         case TrillType::TRILL_LINE:
    1868            0 :             item->symbolLine(SymId::ornamentTrill, SymId::wiggleTrill);
    1869            0 :             break;
    1870            0 :         case TrillType::PRALLPRALL_LINE:
    1871            0 :             item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
    1872            0 :             break;
    1873            0 :         case TrillType::UPPRALL_LINE:
    1874            0 :             item->symbolLine(SymId::ornamentBottomLeftConcaveStroke,
    1875              :                              SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
    1876            0 :             break;
    1877            0 :         case TrillType::DOWNPRALL_LINE:
    1878            0 :             item->symbolLine(SymId::ornamentLeftVerticalStroke,
    1879              :                              SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
    1880            0 :             break;
    1881              :         }
    1882            0 :         Accidental* a = item->trill()->accidental();
    1883            0 :         if (a) {
    1884            0 :             double vertMargin = 0.35 * item->spatium();
    1885            0 :             RectF box = item->symBbox(item->symbols().front());
    1886            0 :             double x = 0;
    1887            0 :             double y = 0;
    1888            0 :             x = 0.5 * (box.width() - a->width());
    1889              :             double minVertDist = accidentalGoesBelow
    1890            0 :                                  ? Shape(box).minVerticalDistance(a->shape())
    1891            0 :                                  : a->shape().minVerticalDistance(Shape(box));
    1892            0 :             y = accidentalGoesBelow ? minVertDist + vertMargin : -minVertDist - vertMargin;
    1893            0 :             a->setPos(x, y);
    1894            0 :             a->setOwnershipParent(item);
    1895              :         }
    1896              :     } else {
    1897            0 :         item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
    1898              :     }
    1899              : 
    1900            0 :     item->setOffset(PointF());
    1901            0 : }
    1902              : 
    1903            0 : void SingleLayout::layout(Vibrato* item, const Context& ctx)
    1904              : {
    1905            0 :     layoutLine(static_cast<SLine*>(item), ctx);
    1906            0 : }
    1907              : 
    1908            0 : void SingleLayout::layout(VibratoSegment* item, const Context&)
    1909              : {
    1910            0 :     VibratoSegment::LayoutData* ldata = item->mutldata();
    1911            0 :     if (item->spanner()->placeBelow()) {
    1912            0 :         ldata->setPosY(0.0);
    1913              :     }
    1914              : 
    1915            0 :     switch (item->vibrato()->vibratoType()) {
    1916            0 :     case VibratoType::GUITAR_VIBRATO:
    1917            0 :         item->symbolLine(SymId::guitarVibratoStroke, SymId::guitarVibratoStroke);
    1918            0 :         break;
    1919            0 :     case VibratoType::GUITAR_VIBRATO_WIDE:
    1920            0 :         item->symbolLine(SymId::guitarWideVibratoStroke, SymId::guitarWideVibratoStroke);
    1921            0 :         break;
    1922            0 :     case VibratoType::VIBRATO_SAWTOOTH:
    1923            0 :         item->symbolLine(SymId::wiggleSawtooth, SymId::wiggleSawtooth);
    1924            0 :         break;
    1925            0 :     case VibratoType::VIBRATO_SAWTOOTH_WIDE:
    1926            0 :         item->symbolLine(SymId::wiggleSawtoothWide, SymId::wiggleSawtoothWide);
    1927            0 :         break;
    1928            0 :     default:
    1929            0 :         break;
    1930              :     }
    1931              : 
    1932            0 :     item->setOffset(PointF());
    1933            0 : }
    1934              : 
    1935            0 : void SingleLayout::layout(Volta* item, const Context& ctx)
    1936              : {
    1937            0 :     layoutLine(item, ctx);
    1938            0 : }
    1939              : 
    1940            0 : void SingleLayout::layout(WhammyBar* item, const Context& ctx)
    1941              : {
    1942            0 :     layoutLine(item, ctx);
    1943            0 : }
    1944              : 
    1945            0 : void SingleLayout::layout(VoltaSegment* item, const Context& ctx)
    1946              : {
    1947            0 :     layoutTextLineBaseSegment(item, ctx);
    1948            0 :     item->setOffset(PointF());
    1949              : 
    1950            0 :     double spatium = ctx.style().spatium();
    1951            0 :     double hookHeight = item->absoluteFromSpatium(item->volta()->beginHookHeight());
    1952            0 :     if (item->text()) {
    1953            0 :         Text* text = item->text();
    1954            0 :         RectF textBBox = text->ldata()->bbox().translated(text->pos());
    1955            0 :         text->mutldata()->moveY(hookHeight - textBBox.bottom());
    1956            0 :         text->mutldata()->moveX(0.5 * spatium);
    1957              :     }
    1958            0 : }
    1959              : 
    1960            0 : void SingleLayout::layout(WhammyBarSegment* item, const Context& ctx)
    1961              : {
    1962            0 :     layoutTextLineBaseSegment(item, ctx);
    1963            0 :     item->setOffset(PointF());
    1964            0 : }
    1965              : 
    1966            0 : void SingleLayout::layout(Text* item, const Context& ctx)
    1967              : {
    1968            0 :     layoutTextBase(toTextBase(item), ctx, item->mutldata());
    1969            0 : }
    1970              : 
    1971            0 : void SingleLayout::layoutTextBase(const TextBase* item, const Context& ctx, TextBase::LayoutData* ldata)
    1972              : {
    1973            0 :     ldata->setPos(PointF());
    1974            0 :     const_cast<TextBase*>(item)->setOffset(PointF());
    1975              : 
    1976            0 :     if (item->placeBelow()) {
    1977            0 :         ldata->setPosY(0.0);
    1978              :     }
    1979              : 
    1980            0 :     layout1TextBase(item, ctx, ldata);
    1981            0 : }
    1982              : 
    1983            0 : static void textHorizontalLayout(const TextBase* item, Shape& shape, double maxBlockWidth, TextBase::LayoutData* ldata)
    1984              : {
    1985              :     // Position and alignment
    1986            0 :     for (size_t i = 0; i < ldata->blocks.size(); ++i) {
    1987            0 :         TextBlock& textBlock = ldata->blocks[i];
    1988            0 :         double xAdj = -textBlock.boundingRect().left();
    1989              : 
    1990              :         // Set position relative to reference point
    1991            0 :         AlignH position = item->position();
    1992            0 :         if (position == AlignH::HCENTER) {
    1993            0 :             xAdj -= maxBlockWidth * .5;
    1994            0 :         } else if (position == AlignH::RIGHT) {
    1995            0 :             xAdj -= maxBlockWidth;
    1996              :         }
    1997              : 
    1998            0 :         double diff = maxBlockWidth - textBlock.boundingRect().width();
    1999            0 :         if (muse::RealIsNull(diff)) {
    2000              :             // This is the longest line, don't align
    2001            0 :             for (TextFragment& f : textBlock.fragments()) {
    2002            0 :                 f.pos.rx() += xAdj;
    2003              :             }
    2004            0 :             textBlock.shape().translate(PointF(xAdj, 0.0));
    2005            0 :             shape.add(textBlock.shape().translated(PointF(0.0, textBlock.y())));
    2006            0 :             continue;
    2007            0 :         }
    2008              :         // Align relative to the longest line
    2009            0 :         AlignH alignH = item->align().horizontal;
    2010            0 :         if (alignH == AlignH::HCENTER) {
    2011            0 :             xAdj += diff * 0.5;
    2012            0 :         } else if (alignH == AlignH::RIGHT) {
    2013            0 :             xAdj += diff;
    2014              :         }
    2015              : 
    2016            0 :         for (TextFragment& fragment : textBlock.fragments()) {
    2017            0 :             fragment.pos.rx() += xAdj;
    2018              :         }
    2019            0 :         textBlock.shape().translate(PointF(xAdj, 0.0));
    2020            0 :         shape.add(textBlock.shape().translated(PointF(0.0, textBlock.y())));
    2021              :     }
    2022            0 : }
    2023              : 
    2024            0 : void SingleLayout::layout1TextBase(const TextBase* item, const Context&, TextBase::LayoutData* ldata)
    2025              : {
    2026            0 :     if (ldata->layoutInvalid) {
    2027            0 :         item->createBlocks(ldata);
    2028              :     }
    2029              : 
    2030            0 :     double y = 0;
    2031              : 
    2032            0 :     double maxBlockWidth = -DBL_MAX;
    2033              :     // adjust the bounding box for the text item
    2034            0 :     for (size_t i = 0; i < ldata->rows(); ++i) {
    2035            0 :         TextBlock& t = ldata->blocks[i];
    2036            0 :         TextLayout::layoutTextBlock(&t, item);
    2037            0 :         const RectF* r = &t.boundingRect();
    2038              : 
    2039            0 :         if (r->height() == 0) {
    2040            0 :             r = &ldata->blocks.at(0).boundingRect();
    2041              :         }
    2042            0 :         y += t.lineSpacing();
    2043            0 :         t.setY(y);
    2044            0 :         maxBlockWidth = std::max(maxBlockWidth, t.boundingRect().width());
    2045              :     }
    2046              : 
    2047            0 :     Shape shape;
    2048            0 :     textHorizontalLayout(item, shape, maxBlockWidth, ldata);
    2049              : 
    2050            0 :     RectF bb = shape.bbox();
    2051              : 
    2052            0 :     double yoff = 0;
    2053            0 :     double h    = 0;
    2054              : 
    2055            0 :     ldata->setPos(PointF());
    2056              : 
    2057            0 :     if (item->align() == AlignV::BOTTOM) {
    2058            0 :         yoff += h - bb.bottom();
    2059            0 :     } else if (item->align() == AlignV::VCENTER) {
    2060            0 :         yoff +=  (h - (bb.top() + bb.bottom())) * .5;
    2061            0 :     } else if (item->align() == AlignV::BASELINE) {
    2062            0 :         yoff += h * .5 - ldata->blocks.front().lineSpacing();
    2063              :     } else {
    2064            0 :         yoff += -bb.top();
    2065              :     }
    2066              : 
    2067            0 :     for (TextBlock& t : ldata->blocks) {
    2068            0 :         t.setY(t.y() + yoff);
    2069              :     }
    2070              : 
    2071            0 :     shape.translateY(yoff);
    2072            0 :     ldata->setShape(shape);
    2073              : 
    2074            0 :     if (item->hasFrame()) {
    2075            0 :         item->layoutFrame(ldata);
    2076              :     }
    2077              : 
    2078            0 :     if (!muse::RealIsNull(item->textAngle())) {
    2079            0 :         Transform t;
    2080            0 :         t.rotate(item->textAngle());
    2081            0 :         ldata->setShape(shape.transform(t));
    2082            0 :         ldata->highResShape.mut_value().transform(t);
    2083              :     }
    2084            0 : }
    2085              : 
    2086            0 : void SingleLayout::layoutLine(SLine* item, const Context& ctx)
    2087              : {
    2088            0 :     if (item->spannerSegments().empty()) {
    2089            0 :         item->setLen(ctx.style().spatium() * 8);
    2090              :     }
    2091              : 
    2092            0 :     LineSegment* lineSegm = item->frontSegment();
    2093            0 :     layoutLineSegment(lineSegm, ctx);
    2094              : 
    2095            0 :     item->setbbox(lineSegm->ldata()->bbox(LD_ACCESS::BAD));
    2096            0 : }
    2097              : 
    2098              : // Extends lines to fill the corner between them.
    2099              : // Assumes that l1p2 == l2p1 is the intersection between the lines.
    2100              : // If checkAngle is false, assumes that the lines are perpendicular,
    2101              : // and some calculations are saved.
    2102            0 : static inline void extendLines(const PointF& l1p1, PointF& l1p2, PointF& l2p1, const PointF& l2p2, double lineWidth, bool checkAngle)
    2103              : {
    2104            0 :     PointF l1UnitVector = (l1p2 - l1p1).normalized();
    2105            0 :     PointF l2UnitVector = (l2p1 - l2p2).normalized();
    2106              : 
    2107            0 :     double addedLength = lineWidth * 0.5;
    2108              : 
    2109            0 :     if (checkAngle) {
    2110            0 :         double angle = M_PI - acos(PointF::dotProduct(l1UnitVector, l2UnitVector));
    2111              : 
    2112            0 :         if (angle <= M_PI_2) {
    2113            0 :             addedLength *= tan(0.5 * angle);
    2114              :         }
    2115              :     }
    2116              : 
    2117            0 :     l1p2 += l1UnitVector * addedLength;
    2118            0 :     l2p1 += l2UnitVector * addedLength;
    2119            0 : }
    2120              : 
    2121            0 : static PolygonF createArrow(bool start, bool filled, PointF& startPoint, PointF& endPoint, const TextLineBase* tl)
    2122              : {
    2123            0 :     double arrowWidth = 0.0;
    2124            0 :     double arrowHeight = 0.0;
    2125            0 :     if (filled) {
    2126            0 :         arrowWidth = tl->absoluteFromSpatium(start ? tl->beginFilledArrowWidth() : tl->endFilledArrowWidth());
    2127            0 :         arrowHeight = tl->absoluteFromSpatium(start ? tl->beginFilledArrowHeight() : tl->endFilledArrowHeight());
    2128              :     } else {
    2129            0 :         arrowWidth = tl->absoluteFromSpatium(start ? tl->beginLineArrowWidth() : tl->endLineArrowWidth());
    2130            0 :         arrowHeight = tl->absoluteFromSpatium(start ? tl->beginLineArrowHeight() : tl->endLineArrowHeight());
    2131              :     }
    2132              : 
    2133            0 :     PolygonF arrow;
    2134            0 :     if (start) {
    2135            0 :         arrow << PointF(0.0, -arrowHeight / 2) << PointF(-arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2);  // left
    2136              :     } else {
    2137            0 :         arrow << PointF(0.0, -arrowHeight / 2) << PointF(arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2);  // right
    2138              :     }
    2139              : 
    2140            0 :     PointF arrowAdjust = PointF(arrowWidth, 0.0);
    2141            0 :     arrowAdjust = (start ? 1.0 : -1.0) * arrowAdjust;
    2142            0 :     arrow.translate(arrowAdjust);
    2143              : 
    2144            0 :     const double yDiff = endPoint.y() - startPoint.y();
    2145            0 :     const double xDiff = endPoint.x() - startPoint.x();
    2146            0 :     const double rotate = atan(yDiff / xDiff) + (endPoint.x() < startPoint.x() ? M_PI : 0.0);
    2147              : 
    2148            0 :     Transform t;
    2149            0 :     t.rotateRadians(rotate);
    2150              : 
    2151            0 :     for (PointF& p : arrow) {
    2152            0 :         p = t.map(p);
    2153              :     }
    2154              : 
    2155            0 :     if (start) {
    2156            0 :         arrow.translate(startPoint);
    2157              :     } else {
    2158            0 :         arrow.translate(endPoint);
    2159              :     }
    2160              : 
    2161            0 :     const PointF lineVector = endPoint - startPoint;
    2162            0 :     const PointF unitVector = lineVector.normalized();
    2163            0 :     const double reduction = filled ? arrowWidth : tl->absoluteFromSpatium(tl->lineWidth()) / 2;
    2164            0 :     if (start) {
    2165            0 :         startPoint += reduction * unitVector;
    2166              :     } else {
    2167            0 :         endPoint -= reduction * unitVector;
    2168              :     }
    2169              : 
    2170            0 :     return arrow;
    2171            0 : }
    2172              : 
    2173            0 : void SingleLayout::layoutTextLineBaseSegment(TextLineBaseSegment* item, const Context& ctx)
    2174              : {
    2175            0 :     TextLineBaseSegment::LayoutData* ldata = item->mutldata();
    2176            0 :     ldata->npoints = 0;
    2177            0 :     const TextLineBase* tl = item->textLineBase();
    2178            0 :     const double spatium = tl->spatium();
    2179            0 :     const bool isSingleOrBegin = item->isSingleBeginType();
    2180            0 :     const bool isSingleOrEnd = item->isSingleEndType();
    2181            0 :     const bool isDottedLine = tl->lineStyle() == LineType::DOTTED;
    2182            0 :     const double lineWidth = tl->absoluteFromSpatium(tl->lineWidth());
    2183              : 
    2184            0 :     if (!tl->diagonal()) {
    2185            0 :         item->setUserYoffset2(0);
    2186              :     }
    2187              : 
    2188            0 :     auto alignBaseLine = [lineWidth](Text* text, PointF& pp1, PointF& pp2) {
    2189            0 :         PointF widthCorrection(0.0, lineWidth / 2);
    2190            0 :         switch (text->align().vertical) {
    2191            0 :         case AlignV::TOP:
    2192            0 :             pp1 += widthCorrection;
    2193            0 :             pp2 += widthCorrection;
    2194            0 :             break;
    2195            0 :         case AlignV::VCENTER:
    2196            0 :             break;
    2197            0 :         case AlignV::BOTTOM:
    2198            0 :             pp1 -= widthCorrection;
    2199            0 :             pp2 -= widthCorrection;
    2200            0 :             break;
    2201            0 :         case AlignV::BASELINE:
    2202            0 :             pp1 -= widthCorrection;
    2203            0 :             pp2 -= widthCorrection;
    2204            0 :             break;
    2205              :         }
    2206            0 :     };
    2207              : 
    2208            0 :     if (isSingleOrBegin) {
    2209            0 :         item->text()->setXmlText(tl->beginText());
    2210            0 :         item->text()->setFamily(tl->beginFontFamily());
    2211            0 :         item->text()->setSize(tl->beginFontSize());
    2212            0 :         item->text()->setSymbolScale(tl->beginTextMusicalSymbolsScale());
    2213            0 :         item->text()->setSymbolSize(tl->beginTextMusicSymbolsSize());
    2214            0 :         item->text()->setOffset(tl->beginTextOffset() * item->mag());
    2215            0 :         item->text()->setAlign(tl->beginTextAlign());
    2216            0 :         item->text()->setPosition(tl->beginTextPosition());
    2217            0 :         item->text()->setFontStyle(tl->beginFontStyle());
    2218              :     } else {
    2219            0 :         item->text()->setXmlText(tl->continueText());
    2220            0 :         item->text()->setFamily(tl->continueFontFamily());
    2221            0 :         item->text()->setSize(tl->continueFontSize());
    2222            0 :         item->text()->setSymbolScale(tl->continueTextMusicalSymbolsScale());
    2223            0 :         item->text()->setSymbolSize(tl->continueTextMusicSymbolsSize());
    2224            0 :         item->text()->setOffset(tl->continueTextOffset() * item->mag());
    2225            0 :         item->text()->setAlign(tl->continueTextAlign());
    2226            0 :         item->text()->setPosition(tl->continueTextPosition());
    2227            0 :         item->text()->setFontStyle(tl->continueFontStyle());
    2228              :     }
    2229            0 :     item->text()->setPlacement(PlacementV::ABOVE);
    2230            0 :     item->text()->setTrack(item->track());
    2231            0 :     item->text()->setColor(tl->lineColor());
    2232            0 :     layout(item->text(), ctx);
    2233              : 
    2234            0 :     if (isSingleOrEnd) {
    2235            0 :         item->endText()->setXmlText(tl->endText());
    2236            0 :         item->endText()->setFamily(tl->endFontFamily());
    2237            0 :         item->endText()->setSize(tl->endFontSize());
    2238            0 :         item->endText()->setSymbolScale(tl->endTextMusicalSymbolsScale());
    2239            0 :         item->endText()->setSymbolSize(tl->endTextMusicSymbolsSize());
    2240            0 :         item->endText()->setOffset(tl->endTextOffset() * item->mag());
    2241            0 :         item->endText()->setAlign(tl->endTextAlign());
    2242            0 :         item->endText()->setPosition(tl->endTextPosition());
    2243            0 :         item->endText()->setFontStyle(tl->endFontStyle());
    2244            0 :         item->endText()->setPlacement(PlacementV::ABOVE);
    2245            0 :         item->endText()->setTrack(item->track());
    2246            0 :         item->endText()->setColor(tl->lineColor());
    2247            0 :         layout(item->endText(), ctx);
    2248              :     } else {
    2249            0 :         item->endText()->setXmlText(u"");
    2250              :     }
    2251              : 
    2252            0 :     if (!item->textLineBase()->textSizeSpatiumDependent()) {
    2253            0 :         item->text()->setSize(item->text()->size() * item->defaultSpatium() / item->spatium());
    2254            0 :         item->endText()->setSize(item->endText()->size() * item->defaultSpatium() / item->spatium());
    2255              :     }
    2256              : 
    2257            0 :     PointF pp1;
    2258            0 :     PointF pp2(item->pos2());
    2259              : 
    2260              :     // line with no text or hooks - just use the basic rectangle for line
    2261            0 :     if (item->text()->empty() && item->endText()->empty()
    2262            0 :         && (!isSingleOrBegin || tl->beginHookType() == HookType::NONE)
    2263            0 :         && (!isSingleOrEnd || tl->endHookType() == HookType::NONE)) {
    2264            0 :         ldata->npoints = 2;
    2265            0 :         ldata->points[0] = pp1;
    2266            0 :         ldata->points[1] = pp2;
    2267            0 :         ldata->lineLength = sqrt(PointF::dotProduct(pp2 - pp1, pp2 - pp1));
    2268              : 
    2269            0 :         item->setbbox(TextLineBaseSegment::boundingBoxOfLine(pp1, pp2, lineWidth / 2, isDottedLine));
    2270            0 :         return;
    2271              :     }
    2272              : 
    2273            0 :     double l1 = 0.0;
    2274            0 :     double l2 = 0.0;
    2275            0 :     const double gapBetweenTextAndLine = spatium * tl->gapBetweenTextAndLine().val();
    2276              : 
    2277            0 :     const bool alignBeginText = tl->beginTextPlace() == TextPlace::LEFT || tl->beginTextPlace() == TextPlace::AUTO;
    2278            0 :     const bool alignContinueText = tl->continueTextPlace() == TextPlace::LEFT || tl->continueTextPlace() == TextPlace::AUTO;
    2279            0 :     const bool alignEndText = tl->endTextPlace() == TextPlace::LEFT || tl->endTextPlace() == TextPlace::AUTO;
    2280            0 :     const bool hasBeginText = !item->text()->empty() && isSingleOrBegin;
    2281            0 :     const bool hasContinueText = !item->text()->empty() && !isSingleOrBegin;
    2282            0 :     const bool hasEndText = !item->endText()->empty() && isSingleOrEnd;
    2283              : 
    2284            0 :     Shape shape;
    2285              : 
    2286            0 :     if (!item->text()->empty()) {
    2287            0 :         if ((isSingleOrBegin && alignBeginText) || (!isSingleOrBegin && alignContinueText)) {
    2288            0 :             l1 = gapBetweenTextAndLine;
    2289            0 :             switch (item->text()->position()) {
    2290            0 :             case AlignH::LEFT:
    2291            0 :                 l1 += item->text()->ldata()->bbox().width();
    2292            0 :                 break;
    2293            0 :             case AlignH::HCENTER:
    2294            0 :                 l1 += item->text()->ldata()->bbox().width() / 2;
    2295            0 :                 break;
    2296            0 :             default:
    2297            0 :                 break;
    2298              :             }
    2299              :         }
    2300            0 :         shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item);
    2301              :     }
    2302              :     // set end text position and extend bbox
    2303            0 :     if (!item->endText()->empty()) {
    2304            0 :         if (alignEndText) {
    2305            0 :             l2 = gapBetweenTextAndLine;
    2306            0 :             switch (item->endText()->position()) {
    2307            0 :             case AlignH::RIGHT:
    2308            0 :                 l2 += item->endText()->ldata()->bbox().width();
    2309            0 :                 break;
    2310            0 :             case AlignH::HCENTER:
    2311            0 :                 l2 += item->endText()->ldata()->bbox().width() / 2;
    2312            0 :                 break;
    2313            0 :             default:
    2314            0 :                 break;
    2315              :             }
    2316              :         }
    2317            0 :         const double endTextX = std::max(pp2.x(), shape.bbox().right() + l2); // prevent end text from overlapping begin text
    2318            0 :         item->endText()->mutldata()->setPosX(endTextX);
    2319            0 :         shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item);
    2320              :     }
    2321              : 
    2322            0 :     bool beginHookDrawnSeparately = false;
    2323            0 :     bool endHookDrawnSeparately = false;
    2324              : 
    2325            0 :     if (tl->lineVisible()) {
    2326            0 :         pp1.rx() += l1;
    2327            0 :         pp2.rx() -= l2;
    2328              : 
    2329              :         // Make sure baseline of text and line are properly aligned (accounting for line thickness)
    2330            0 :         if ((hasBeginText && alignBeginText) || (hasContinueText && alignContinueText)) {
    2331            0 :             alignBaseLine(item->text(), pp1, pp2);
    2332            0 :         } else if (hasEndText && alignEndText) {
    2333            0 :             alignBaseLine(item->endText(), pp1, pp2);
    2334              :         }
    2335              : 
    2336            0 :         const double beginHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->beginHookHeight().val() * spatium;
    2337            0 :         const double endHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->endHookHeight().val() * spatium;
    2338              : 
    2339            0 :         double beginHookWidth = 0.0;
    2340            0 :         double endHookWidth = 0.0;
    2341              : 
    2342            0 :         if (tl->beginHookType() == HookType::HOOK_45) {
    2343            0 :             beginHookWidth = std::fabs(beginHookHeight * .4);
    2344            0 :             pp1.rx() += beginHookWidth;
    2345              :         }
    2346              : 
    2347            0 :         if (tl->endHookType() == HookType::HOOK_45) {
    2348            0 :             endHookWidth = std::fabs(endHookHeight * .4);
    2349            0 :             pp2.rx() -= endHookWidth;
    2350              :         }
    2351              : 
    2352              :         // don't draw backwards lines (or hooks) if text is longer than nominal line length
    2353            0 :         if (!item->text()->empty() && pp1.x() > pp2.x() && !tl->diagonal()) {
    2354            0 :             ldata->setShape(shape);
    2355            0 :             return;
    2356              :         }
    2357              : 
    2358              :         const bool beginArrow = isSingleOrBegin
    2359            0 :                                 && (tl->beginHookType() == HookType::ARROW_FILLED || tl->beginHookType() == HookType::ARROW);
    2360            0 :         const bool endArrow = isSingleOrEnd && (tl->endHookType() == HookType::ARROW_FILLED || tl->endHookType() == HookType::ARROW);
    2361              : 
    2362            0 :         if (beginArrow) {
    2363            0 :             const bool filled = tl->beginHookType() == HookType::ARROW_FILLED;
    2364            0 :             ldata->beginArrow = createArrow(true, filled, pp1, pp2, tl);
    2365            0 :             shape.add(ldata->beginArrow.boundingRect(), item);
    2366              :         }
    2367            0 :         if (endArrow) {
    2368            0 :             const bool filled = tl->endHookType() == HookType::ARROW_FILLED;
    2369            0 :             ldata->endArrow = createArrow(false, filled, pp1, pp2, tl);
    2370            0 :             shape.add(ldata->endArrow.boundingRect(), item);
    2371              :         }
    2372              : 
    2373            0 :         auto hasHook = [](HookType type) -> bool {
    2374            0 :             switch (type) {
    2375            0 :             case HookType::HOOK_45:
    2376              :             case HookType::HOOK_90:
    2377              :             case HookType::HOOK_90T:
    2378            0 :                 return true;
    2379            0 :             case HookType::NONE:
    2380              :             case HookType::ARROW:
    2381              :             case HookType::ARROW_FILLED:
    2382              :             case HookType::ROSETTE:
    2383            0 :                 return false;
    2384            0 :             default:
    2385            0 :                 break;
    2386              :             }
    2387            0 :             return false;
    2388              :         };
    2389              : 
    2390            0 :         const bool beginHook = isSingleOrBegin && hasHook(tl->beginHookType());
    2391            0 :         const bool endHook = isSingleOrEnd && hasHook(tl->endHookType());
    2392              : 
    2393            0 :         if (beginHook) {
    2394              :             // We use the term "endpoint" for the point that does not touch the main line.
    2395            0 :             const PointF& beginHookEndpoint = ldata->points[ldata->npoints++]
    2396            0 :                                                   = PointF(pp1.x() - beginHookWidth, pp1.y() + beginHookHeight);
    2397            0 :             if (tl->beginHookType() == HookType::HOOK_90T) {
    2398              :                 // A T-hook needs to be drawn separately, so we add an extra point
    2399            0 :                 beginHookDrawnSeparately = true;
    2400            0 :                 ldata->points[ldata->npoints++] = PointF(pp1.x() - beginHookWidth, pp1.y() - beginHookHeight);
    2401            0 :             } else if (tl->lineStyle() != LineType::SOLID) {
    2402              :                 // For non-solid lines, we also draw the hook separately,
    2403              :                 // so that we can distribute the dashes/dots for each linepiece individually
    2404            0 :                 beginHookDrawnSeparately = true;
    2405            0 :                 PointF& beginHookStartpoint = ldata->points[ldata->npoints++] = pp1;
    2406              : 
    2407            0 :                 if (tl->lineStyle() == LineType::DASHED) {
    2408              :                     // For dashes lines, we extend the lines somewhat,
    2409              :                     // so that the corner between them gets filled
    2410            0 :                     bool checkAngle = tl->beginHookType() == HookType::HOOK_45 || tl->diagonal();
    2411            0 :                     extendLines(beginHookEndpoint, beginHookStartpoint, pp1, pp2, lineWidth, checkAngle);
    2412              :                 }
    2413              :             }
    2414              :         }
    2415              : 
    2416            0 :         ldata->points[ldata->npoints++] = pp1;
    2417            0 :         PointF& pp22 = ldata->points[ldata->npoints++] = pp2; // Keep a reference so that we can modify later
    2418              : 
    2419            0 :         if (endHook) {
    2420            0 :             const PointF endHookEndpoint = PointF(pp2.x() + endHookWidth, pp2.y() + endHookHeight);
    2421            0 :             if (tl->endHookType() == HookType::HOOK_90T) {
    2422              :                 // A T-hook needs to be drawn separately, so we add an extra point
    2423            0 :                 endHookDrawnSeparately = true;
    2424            0 :                 ldata->points[ldata->npoints++] = PointF(pp2.x() + endHookWidth, pp2.y() - endHookHeight);
    2425            0 :             } else if (tl->lineStyle() != LineType::SOLID) {
    2426              :                 // For non-solid lines, we also draw the hook separately,
    2427              :                 // so that we can distribute the dashes/dots for each linepiece individually
    2428            0 :                 endHookDrawnSeparately = true;
    2429            0 :                 PointF& endHookStartpoint = ldata->points[ldata->npoints++] = pp2;
    2430              : 
    2431            0 :                 if (tl->lineStyle() == LineType::DASHED) {
    2432              :                     // For dashes lines, we extend the lines somewhat,
    2433              :                     // so that the corner between them gets filled
    2434            0 :                     bool checkAngle = tl->endHookType() == HookType::HOOK_45 || tl->diagonal();
    2435            0 :                     extendLines(pp1, pp22, endHookStartpoint, endHookEndpoint, lineWidth, checkAngle);
    2436              :                 }
    2437              :             }
    2438              : 
    2439            0 :             ldata->points[ldata->npoints++] = endHookEndpoint;
    2440              :         }
    2441              : 
    2442            0 :         ldata->lineLength = sqrt(PointF::dotProduct(pp22 - pp1, pp22 - pp1));
    2443              :     }
    2444              : 
    2445              :     // Calculate shape of line
    2446              :     {
    2447            0 :         int start = 0;
    2448            0 :         if (beginHookDrawnSeparately) {
    2449            0 :             PointF& p1 = ldata->points[start++];
    2450            0 :             PointF& p2 = ldata->points[start++];
    2451            0 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
    2452              :         }
    2453              : 
    2454            0 :         int end = ldata->npoints;
    2455            0 :         if (endHookDrawnSeparately) {
    2456            0 :             PointF& p1 = ldata->points[--end];
    2457            0 :             PointF& p2 = ldata->points[--end];
    2458            0 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
    2459              :         }
    2460              : 
    2461            0 :         for (int i = start; i < end - 1; ++i) {
    2462            0 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lineWidth / 2,
    2463            0 :                                                              isDottedLine), item, !tl->lineVisible());
    2464              :         }
    2465              :     }
    2466              : 
    2467            0 :     ldata->setShape(shape);
    2468            0 : }
        

Generated by: LCOV version 2.0-1