    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2002-2016 Werner Schweer
    //
    //  This program is free software; you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License version 2
    //  as published by the Free Software Foundation and appearing in
    //  the file LICENCE.GPL
    //=============================================================================

    #include "accidental.h"
    #include "barline.h"
    #include "beam.h"
    #include "box.h"
    #include "chord.h"
    #include "clef.h"
    #include "element.h"
    #include "fingering.h"
    #include "glissando.h"
    #include "harmony.h"
    #include "key.h"
    #include "keysig.h"
    #include "layoutbreak.h"
    #include "layout.h"
    #include "lyrics.h"
    #include "marker.h"
    #include "measure.h"
    #include "mscore.h"
    #include "notedot.h"
    #include "note.h"
    #include "ottava.h"
    #include "page.h"
    #include "part.h"
    #include "repeat.h"
    #include "score.h"
    #include "segment.h"
    #include "sig.h"
    #include "slur.h"
    #include "staff.h"
    #include "stem.h"
    #include "style.h"
    #include "sym.h"
    #include "system.h"
    #include "text.h"
    #include "tie.h"
    #include "timesig.h"
    #include "tremolo.h"
    #include "tuplet.h"
    #include "undo.h"
    #include "utils.h"
    #include "volta.h"
    #include "breath.h"
    #include "tempotext.h"
    #include "systemdivider.h"
    #include "hook.h"
    #include "ambitus.h"
    #include "hairpin.h"
    #include "stafflines.h"
    #include "articulation.h"
    #include "bracket.h"

    namespace Ms {

    // #define PAGE_DEBUG

    #ifdef PAGE_DEBUG
    #define PAGEDBG(...)  qDebug(__VA_ARGS__)
    #else
    #define PAGEDBG(...)  ;
    #endif

    //---------------------------------------------------------
    //   rebuildBspTree
    //---------------------------------------------------------

    void Score::rebuildBspTree()
          {
          for (Page* page : pages())
                page->rebuildBspTree();
          }

    //---------------------------------------------------------
    //   layoutChords1
    //    - layout upstem and downstem chords
    //    - offset as necessary to avoid conflict
    //---------------------------------------------------------

    void Score::layoutChords1(Segment* segment, int staffIdx)
          {
          Staff* staff = Score::staff(staffIdx);

          if (staff->isTabStaff(segment->tick()))
                return;

          std::vector<Note*> upStemNotes;
          std::vector<Note*> downStemNotes;
          int upVoices       = 0;
          int downVoices     = 0;
          int startTrack     = staffIdx * VOICES;
          int endTrack       = startTrack + VOICES;
          qreal nominalWidth = noteHeadWidth() * staff->mag(segment->tick());
          qreal maxUpWidth   = 0.0;
          qreal maxDownWidth = 0.0;
          qreal maxUpMag     = 0.0;
          qreal maxDownMag   = 0.0;

          // dots and hooks can affect layout of notes as well as vice versa
          int upDots         = 0;
          int downDots       = 0;
          bool upHooks       = false;
          bool downHooks     = false;

          // also check for grace notes
          bool upGrace       = false;
          bool downGrace     = false;

          for (int track = startTrack; track < endTrack; ++track) {
                Element* e = segment->element(track);
                if (e && e->isChord()) {
                      Chord* chord = toChord(e);
                      bool hasGraceBefore = false;
                      for (Chord* c : chord->graceNotes()) {
                            if (c->isGraceBefore())
                                  hasGraceBefore = true;
                            layoutChords2(c->notes(), c->up());       // layout grace note noteheads
                            layoutChords3(c->notes(), staff, 0);      // layout grace note chords
                            }
                      if (chord->up()) {
                            ++upVoices;
                            upStemNotes.insert(upStemNotes.end(), chord->notes().begin(), chord->notes().end());
                            upDots   = qMax(upDots, chord->dots());
                            maxUpMag = qMax(maxUpMag, chord->mag());
                            if (!upHooks)
                                  upHooks = chord->hook();
                            if (hasGraceBefore)
                                  upGrace = true;
                            }
                      else {
                            ++downVoices;
                            downStemNotes.insert(downStemNotes.end(), chord->notes().begin(), chord->notes().end());
                            downDots = qMax(downDots, chord->dots());
                            maxDownMag = qMax(maxDownMag, chord->mag());
                            if (!downHooks)
                                  downHooks = chord->hook();
                            if (hasGraceBefore)
                                  downGrace = true;
                            }
                      }
                }

          if (upVoices + downVoices) {
                // TODO: use track as secondary sort criteria?
                // otherwise there might be issues with unisons between voices
                // in some corner cases

                maxUpWidth   = nominalWidth * maxUpMag;
                maxDownWidth = nominalWidth * maxDownMag;

                // layout upstem noteheads
                if (upVoices > 1) {
                      qSort(upStemNotes.begin(), upStemNotes.end(),
                         [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
                      }
                if (upVoices) {
                      qreal hw = layoutChords2(upStemNotes, true);
                      maxUpWidth = qMax(maxUpWidth, hw);
                      }

                // layout downstem noteheads
                if (downVoices > 1) {
                      qSort(downStemNotes.begin(), downStemNotes.end(),
                         [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
                      }
                if (downVoices) {
                      qreal hw = layoutChords2(downStemNotes, false);
                      maxDownWidth = qMax(maxDownWidth, hw);
                      }

                qreal sp                 = staff->spatium(segment->tick());
                qreal upOffset           = 0.0;      // offset to apply to upstem chords
                qreal downOffset         = 0.0;      // offset to apply to downstem chords
                qreal dotAdjust          = 0.0;      // additional chord offset to account for dots
                qreal dotAdjustThreshold = 0.0;      // if it exceeds this amount

                // centering adjustments for whole note, breve, and small chords
                qreal centerUp          = 0.0;      // offset to apply in order to center upstem chords
                qreal oversizeUp        = 0.0;      // adjustment to oversized upstem chord needed if laid out to the right
                qreal centerDown        = 0.0;      // offset to apply in order to center downstem chords
                qreal centerAdjustUp    = 0.0;      // adjustment to upstem chord needed after centering donwstem chord
                qreal centerAdjustDown  = 0.0;      // adjustment to downstem chord needed after centering upstem chord

                // only center chords if they differ from nominal by at least this amount
                // this avoids unnecessary centering on differences due only to floating point roundoff
                // it also allows for the possibility of disabling centering
                // for notes only "slightly" larger than nominal, like half notes
                // but this will result in them not being aligned with each other between voices
                // unless you change to left alignment as described in the comments below
                qreal centerThreshold   = 0.01 * sp;

                // amount by which actual width exceeds nominal, adjusted for staff mag() only
                qreal headDiff = maxUpWidth - nominalWidth;
                // amount by which actual width exceeds nominal, adjusted for staff & chord/note mag()
                qreal headDiff2 = maxUpWidth - nominalWidth * (maxUpMag / staff->mag(segment->tick()));
                if (headDiff > centerThreshold) {
                      // larger than nominal
                      centerUp = headDiff * -0.5;
                      // maxUpWidth is true width, but we no longer will care about that
                      // instead, we care only about portion to right of origin
                      maxUpWidth += centerUp;
                      // to left align rather than center, delete both of the above
                      if (headDiff2 > centerThreshold) {
                            // if max notehead is wider than nominal with chord/note mag() applied
                            // then noteheads extend to left of origin
                            // because stemPosX() is based on nominal width
                            // so we need to correct for that too
                            centerUp += headDiff2;
                            oversizeUp = headDiff2;
                            }
                      }
                else if (-headDiff > centerThreshold) {
                      // smaller than nominal
                      centerUp = -headDiff * 0.5;
                      if (headDiff2 > centerThreshold) {
                            // max notehead is wider than nominal with chord/note mag() applied
                            // perform same adjustment as above
                            centerUp += headDiff2;
                            oversizeUp = headDiff2;
                            }
                      centerAdjustDown = centerUp;
                      }

                headDiff = maxDownWidth - nominalWidth;
                if (headDiff > centerThreshold) {
                      // larger than nominal
                      centerDown = headDiff * -0.5;
                      // to left align rather than center, change the above to
                      //centerAdjustUp = headDiff;
                      maxDownWidth = nominalWidth - centerDown;
                      }
                else if (-headDiff > centerThreshold) {
                      // smaller than nominal
                      centerDown = -headDiff * 0.5;
                      centerAdjustUp = centerDown;
                      }

                // handle conflict between upstem and downstem chords

                if (upVoices && downVoices) {
                      Note* bottomUpNote = upStemNotes.front();
                      Note* topDownNote  = downStemNotes.back();
                      int separation;
                      if (bottomUpNote->chord()->staffMove() == topDownNote->chord()->staffMove())
                            separation = topDownNote->line() - bottomUpNote->line();
                      else
                            separation = 2;   // no conflict
                      QVector<Note*> overlapNotes;
                      overlapNotes.reserve(8);

                      if (separation == 1) {
                            // second
                            downOffset = maxUpWidth;
                            // align stems if present, leave extra room if not
                            if (topDownNote->chord()->stem() && bottomUpNote->chord()->stem())
                                  downOffset -= topDownNote->chord()->stem()->lineWidth();
                            else
                                  downOffset += 0.1 * sp;
                            }

                      else if (separation < 1) {

                            // overlap (possibly unison)

                            // build list of overlapping notes
                            for (int i = 0, n = upStemNotes.size(); i < n; ++i) {
                                  if (upStemNotes[i]->line() >= topDownNote->line() - 1)
                                        overlapNotes.append(upStemNotes[i]);
                                  else
                                        break;
                                  }
                            for (int i = downStemNotes.size() - 1; i >= 0; --i) {
                                  if (downStemNotes[i]->line() <= bottomUpNote->line() + 1)
                                        overlapNotes.append(downStemNotes[i]);
                                  else
                                        break;
                                  }
                            qSort(overlapNotes.begin(), overlapNotes.end(),
                               [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );

                            // determine nature of overlap
                            bool shareHeads = true;       // can all overlapping notes share heads?
                            bool matchPending = false;    // looking for a unison match
                            bool conflictUnison = false;  // unison found
                            bool conflictSecondUpHigher = false;      // second found
                            bool conflictSecondDownHigher = false;    // second found
                            int lastLine = 1000;
                            Note* p = overlapNotes[0];
                            for (int i = 0, count = overlapNotes.size(); i < count; ++i) {
                                  Note* n = overlapNotes[i];
                                  NoteHead::Type nHeadType;
                                  NoteHead::Type pHeadType;
                                  Chord* nchord = n->chord();
                                  Chord* pchord = p->chord();
                                  if (n->mirror()) {
                                        if (separation < 0) {
                                              // don't try to share heads if there is any mirroring
                                              shareHeads = false;
                                              // don't worry about conflicts involving mirrored notes
                                              continue;
                                              }
                                        }
                                  int line = n->line();
                                  int d = lastLine - line;
                                  switch (d) {
                                        case 0:
                                              // unison
                                              conflictUnison = true;
                                              matchPending = false;
                                              nHeadType = (n->headType() == NoteHead::Type::HEAD_AUTO) ? n->chord()->durationType().headType() : n->headType();
                                              pHeadType = (p->headType() == NoteHead::Type::HEAD_AUTO) ? p->chord()->durationType().headType() : p->headType();
                                              // the most important rules for sharing noteheads on unisons between voices are
                                              // that notes must be one same line with same tpc
                                              // noteheads must be unmirrored and of same group
                                              // and chords must be same size (or else sharing code won't work)
                                              if (n->headGroup() != p->headGroup() || n->tpc() != p->tpc() || n->mirror() || p->mirror() || nchord->small() != pchord->small()) {
                                                    shareHeads = false;
                                                    }
                                              else {
                                                    // noteheads are potentially shareable
                                                    // it is more subjective at this point
                                                    // current default is to require *either* of the following:
                                                    //    1) both chords have same number of dots, both have stems, and both noteheads are same type and are full size (automatic match)
                                                    // or 2) one or more of the noteheads is not of type AUTO, but is explicitly set to match the other (user-forced match)
                                                    // or 3) exactly one of the noteheads is invisible (user-forced match)
                                                    // thus user can force notes to be shared despite differing number of dots or either being stemless
                                                    // by setting one of the notehead types to match the other or by making one notehead invisible
                                                    // TODO: consider adding a style option, staff properties, or note property to control sharing
                                                    if ((nchord->dots() != pchord->dots() || !nchord->stem() || !pchord->stem() || nHeadType != pHeadType || n->small() || p->small()) &&
                                                        ((n->headType() == NoteHead::Type::HEAD_AUTO && p->headType() == NoteHead::Type::HEAD_AUTO) || nHeadType != pHeadType) &&
                                                        (n->visible() == p->visible())) {
                                                          shareHeads = false;
                                                          }
                                                    }
                                              break;
                                        case 1:
                                              // second
                                              // trust that this won't be a problem for single unison
                                              if (separation < 0) {
                                                    if (n->chord()->up())
                                                          conflictSecondUpHigher = true;
                                                    else
                                                          conflictSecondDownHigher = true;
                                                    shareHeads = false;
                                                    }
                                              break;
                                        default:
                                              // no conflict
                                              if (matchPending)
                                                    shareHeads = false;
                                              matchPending = true;
                                        }
                                  p = n;
                                  lastLine = line;
                                  }
                            if (matchPending)
                                  shareHeads = false;

                            // calculate offsets
                            if (shareHeads) {
                                  for (int i = overlapNotes.size() - 1; i >= 1; i -= 2) {
                                        Note* p = overlapNotes[i-1];
                                        Note* n = overlapNotes[i];
                                        if (!(p->chord()->isNudged() || n->chord()->isNudged())) {
                                              if (p->chord()->dots() == n->chord()->dots()) {
                                                    // hide one set dots
                                                    bool onLine = !(p->line() & 1);
                                                    if (onLine) {
                                                          // hide dots for lower voice
                                                          if (p->voice() & 1)
                                                                p->setDotsHidden(true);
                                                          else
                                                                n->setDotsHidden(true);
                                                          }
                                                    else {
                                                          // hide dots for upper voice
                                                          if (!(p->voice() & 1))
                                                                p->setDotsHidden(true);
                                                          else
                                                                n->setDotsHidden(true);
                                                          }
                                                    }
                                              // formerly we hid noteheads in an effort to fix playback
                                              // but this doesn't work for cases where noteheads cannot be shared
                                              // so better to solve the problem elsewhere
                                              }
                                        }
                                  }
                            else if (conflictUnison && separation == 0 && (!downGrace || upGrace))
                                  downOffset = maxUpWidth + 0.3 * sp;
                            else if (conflictUnison)
                                  upOffset = maxDownWidth + 0.3 * sp;
                            else if (conflictSecondUpHigher)
                                  upOffset = maxDownWidth + 0.2 * sp;
                            else if ((downHooks && !upHooks) && !(upDots && !downDots))
                                  downOffset = maxUpWidth + 0.3 * sp;
                            else if (conflictSecondDownHigher) {
                                  if (downDots && !upDots)
                                        downOffset = maxUpWidth + 0.3 * sp;
                                  else {
                                        upOffset = maxDownWidth - 0.2 * sp;
                                        if (downHooks)
                                              upOffset += 0.3 * sp;
                                        }
                                  }
                            else {
                                  // no direct conflict, so parts can overlap (downstem on left)
                                  // just be sure that stems clear opposing noteheads
                                  qreal clearLeft = 0.0, clearRight = 0.0;
                                  if (topDownNote->chord()->stem())
                                        clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
                                  if (bottomUpNote->chord()->stem())
                                        clearRight = bottomUpNote->chord()->stem()->lineWidth() + qMax(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
                                  else
                                        downDots = 0; // no need to adjust for dots in this case
                                  upOffset = qMax(clearLeft, clearRight);
                                  if (downHooks) {
                                        // we will need more space to avoid collision with hook
                                        // but we won't need as much dot adjustment
                                        upOffset = qMax(upOffset, maxDownWidth + 0.1 * sp);
                                        dotAdjustThreshold = maxUpWidth - 0.3 * sp;
                                        }
                                  // if downstem chord is small, don't center
                                  // and we might not need as much dot adjustment either
                                  if (centerDown > 0.0) {
                                        centerDown = 0.0;
                                        centerAdjustUp = 0.0;
                                        dotAdjustThreshold = (upOffset - maxDownWidth) + maxUpWidth - 0.3 * sp;
                                        }
                                  }

                            }

                      // adjust for dots
                      if ((upDots && !downDots) || (downDots && !upDots)) {
                            // only one sets of dots
                            // place between chords
                            int dots;
                            qreal mag;
                            if (upDots) {
                                  dots = upDots;
                                  mag = maxUpMag;
                                  }
                            else {
                                  dots = downDots;
                                  mag = maxDownMag;
                                  }
                            qreal dotWidth = segment->symWidth(SymId::augmentationDot);
                            // first dot
                            dotAdjust = styleP(StyleIdx::dotNoteDistance) + dotWidth;
                            // additional dots
                            if (dots > 1)
                                  dotAdjust += styleP(StyleIdx::dotDotDistance) * (dots - 1);
                            dotAdjust *= mag;
                            // only by amount over threshold
                            dotAdjust = qMax(dotAdjust - dotAdjustThreshold, 0.0);
                            }
                      if (separation == 1)
                            dotAdjust += 0.1 * sp;

                      }

                // apply chord offsets
                for (int track = startTrack; track < endTrack; ++track) {
                      Element* e = segment->element(track);
                      if (e && e->isChord()) {
                            Chord* chord = toChord(e);
                            if (chord->up()) {
                                  if (upOffset != 0.0) {
                                        chord->rxpos() += upOffset + centerAdjustUp + oversizeUp;
                                        if (downDots && !upDots)
                                              chord->rxpos() += dotAdjust;
                                        }
                                  else
                                        chord->rxpos() += centerUp;
                                  }
                            else {
                                  if (downOffset != 0.0) {
                                        chord->rxpos() += downOffset + centerAdjustDown;
                                        if (upDots && !downDots)
                                              chord->rxpos() += dotAdjust;
                                        }
                                  else
                                        chord->rxpos() += centerDown;
                                  }
                            }
                      }

                // layout chords
                std::vector<Note*> notes;
                if (upVoices)
                      notes.insert(notes.end(), upStemNotes.begin(), upStemNotes.end());
                if (downVoices)
                      notes.insert(notes.end(), downStemNotes.begin(), downStemNotes.end());
                if (upVoices + downVoices > 1)
                      qSort(notes.begin(), notes.end(),
                         [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
                layoutChords3(notes, staff, segment);
                }

          for (int track = startTrack; track < endTrack; ++track) {
                Element* e = segment->element(track);
                if (e)
                      e->layout();
                }
          }

    //---------------------------------------------------------
    //   layoutChords2
    //    - determine which notes need mirroring
    //    - this is called once for each stem direction
    //      eg, once for voices 1&3, once for 2&4
    //      with all notes combined and sorted to resemble one chord
    //    - return maximum non-mirrored notehead width
    //---------------------------------------------------------

    qreal Score::layoutChords2(std::vector<Note*>& notes, bool up)
          {
          int startIdx, endIdx, incIdx;
          qreal maxWidth = 0.0;

          // loop in correct direction so that first encountered notehead wins conflict
          if (up) {
                // loop bottom up
                startIdx = 0;
                endIdx = notes.size();
                incIdx = 1;
                }
          else {
                // loop top down
                startIdx = notes.size() - 1;
                endIdx = -1;
                incIdx = -1;
                }

          int ll        = 1000;         // line of previous notehead
                                        // hack: start high so first note won't show as conflict
          bool lvisible = false;        // was last note visible?
          bool mirror   = false;        // should current notehead be mirrored?
                                        // value is retained and may be used on next iteration
                                        // to track mirror status of previous note
          bool isLeft   = notes[startIdx]->chord()->up();             // is notehead on left?
          int lmove     = notes[startIdx]->chord()->staffMove();      // staff offset of last note (for cross-staff beaming)

          for (int idx = startIdx; idx != endIdx; idx += incIdx) {
                Note* note    = notes[idx];                     // current note
                int line      = note->line();                   // line of current note
                Chord* chord  = note->chord();
                int move      = chord->staffMove();             // staff offset of current note

                // there is a conflict
                // if this is same or adjacent line as previous note (and chords are on same staff!)
                // but no need to do anything about it if either note is invisible
                bool conflict = (qAbs(ll - line) < 2) && (lmove == move) && note->visible() && lvisible;

                // this note is on opposite side of stem as previous note
                // if there is a conflict
                // or if this the first note *after* a conflict
                if (conflict || (chord->up() != isLeft))
                      isLeft = !isLeft;

                // determine if we would need to mirror current note
                // to get it to the correct side
                // this would be needed to get a note to left or downstem or right of upstem
                // whether or not we actually do this is determined later (based on user mirror property)
                bool nmirror = (chord->up() != isLeft);

                // by default, notes and dots are not hidden
                // this may be changed later to allow unisons to share noteheads
                note->setHidden(false);
                note->setDotsHidden(false);

                // be sure chord position is initialized
                // chord may be moved to the right later
                // if there are conflicts between voices
                chord->rxpos() = 0.0;

                // let user mirror property override the default we calculated
                if (note->userMirror() == MScore::DirectionH::AUTO) {
                      mirror = nmirror;
                      }
                else {
                      mirror = note->chord()->up();
                      if (note->userMirror() == MScore::DirectionH::LEFT)
                            mirror = !mirror;
                      }
                note->setMirror(mirror);

                // accumulate return value
                if (!mirror)
                      maxWidth = qMax(maxWidth, note->headWidth());

                // prepare for next iteration
                lvisible = note->visible();
                lmove    = move;
                ll       = line;
                }

          return maxWidth;
          }

    //---------------------------------------------------------
    //   AcEl
    //---------------------------------------------------------

    struct AcEl {
          Note* note;
          qreal x;          // actual x position of this accidental relative to origin
          qreal top;        // top of accidental bbox relative to staff
          qreal bottom;     // bottom of accidental bbox relative to staff
          int line;         // line of note
          int next;         // index of next accidental of same pitch class (ascending list)
          qreal width;      // width of accidental
          qreal ascent;     // amount (in sp) vertical strokes extend above body
          qreal descent;    // amount (in sp) vertical strokes extend below body
          qreal rightClear; // amount (in sp) to right of last vertical stroke above body
          qreal leftClear;  // amount (in sp) to left of last vertical stroke below body
          };

    //---------------------------------------------------------
    //   resolveAccidentals
    //    lx = calculated position of rightmost edge of left accidental relative to origin
    //---------------------------------------------------------

    static bool resolveAccidentals(AcEl* left, AcEl* right, qreal& lx, qreal pd, qreal sp)
          {
          AcEl* upper;
          AcEl* lower;
          if (left->line >= right->line) {
                upper = right;
                lower = left;
                }
          else {
                upper = left;
                lower = right;
                }

          qreal gap = lower->top - upper->bottom;

          // no conflict at all if there is sufficient vertical gap between accidentals
          // the arrangement of accidentals into columns assumes accidentals an octave apart *do* clear
          if (gap >= pd || lower->line - upper->line >= 7)
                return false;

          qreal allowableOverlap = qMax(upper->descent, lower->ascent) - pd;

          // accidentals that are "close" (small gap or even slight overlap)
          if (qAbs(gap) <= 0.33 * sp) {
                // acceptable with slight offset
                // if one of the accidentals can subsume the overlap
                // and both accidentals allow it
                if (-gap <= allowableOverlap && qMin(upper->descent, lower->ascent) > 0.0) {
                      qreal align = qMin(left->width, right->width);
                      lx = qMin(lx, right->x + align - pd);
                      return true;
                      }
                }

          // amount by which overlapping accidentals will be separated
          // for example, the vertical stems of two flat signs
          // these need more space than we would need between non-overlapping accidentals
          qreal overlapShift = pd * 1.41;

          // accidentals with more significant overlap
          // acceptable if one accidental can subsume overlap
          if (left == lower && -gap <= allowableOverlap) {
                qreal offset = qMax(left->rightClear, right->leftClear);
                offset = qMin(offset, left->width) - overlapShift;
                lx = qMin(lx, right->x + offset);
                return true;
                }

          // accidentals with even more overlap
          // can work if both accidentals can subsume overlap
          if (left == lower && -gap <= upper->descent + lower->ascent - pd) {
                qreal offset = qMin(left->rightClear, right->leftClear) - overlapShift;
                if (offset > 0.0) {
                      lx = qMin(lx, right->x + offset);
                      return true;
                      }
                }

          // otherwise, there is real conflict
          lx = qMin(lx, right->x - pd);
          return true;
          }

    //---------------------------------------------------------
    //   layoutAccidental
    //---------------------------------------------------------

    static qreal layoutAccidental(AcEl* me, AcEl* above, AcEl* below, qreal colOffset, QVector<Note*>& leftNotes, qreal pnd, qreal pd, qreal sp)
          {
          qreal lx = colOffset;
          Accidental* acc = me->note->accidental();
          qreal mag = acc->mag();
          pnd *= mag;
          pd *= mag;

          // extra space for ledger lines
          if (me->line <= -2 || me->line >= me->note->staff()->lines(me->note->chord()->tick()) * 2)
                lx = qMin(lx, -0.2 * sp);

          // clear left notes
          int lns = leftNotes.size();
          for (int i = 0; i < lns; ++i) {
                Note* ln = leftNotes[i];
                int lnLine = ln->line();
                qreal lnTop = (lnLine - 1) * 0.5 * sp;
                qreal lnBottom = lnTop + sp;
                if (me->top - lnBottom <= pnd && lnTop - me->bottom <= pnd) {
                      // undercut note above if possible
                      if (lnBottom - me->top <= me->ascent - pnd)
                            lx = qMin(lx, ln->x() + ln->chord()->x() + me->rightClear);
                      else
                            lx = qMin(lx, ln->x() + ln->chord()->x());
                      }
                else if (lnTop > me->bottom)
                      break;
                }

          // clear other accidentals
          bool conflictAbove = false;
          bool conflictBelow = false;

          if (above)
                conflictAbove = resolveAccidentals(me, above, lx, pd, sp);
          if (below)
                conflictBelow = resolveAccidentals(me, below, lx, pd, sp);
          if (conflictAbove || conflictBelow)
                me->x = lx - acc->width() - acc->bbox().x();
          else if (colOffset != 0.0)
                me->x = lx - pd - acc->width() - acc->bbox().x();
          else
                me->x = lx - pnd - acc->width() - acc->bbox().x();

          return me->x;
          }

    //---------------------------------------------------------
    //   layoutChords3
    //    - calculate positions of notes, accidentals, dots
    //---------------------------------------------------------

    void Score::layoutChords3(std::vector<Note*>& notes, Staff* staff, Segment* segment)
          {
          //---------------------------------------------------
          //    layout accidentals
          //    find column for dots
          //---------------------------------------------------

          QVector<Note*> leftNotes; // notes to left of origin
          leftNotes.reserve(8);
          QVector<AcEl> aclist;       // accidentals
          aclist.reserve(8);

          // track columns of octave-separated accidentals
          int columnBottom[7] = { -1, -1, -1, -1, -1, -1, -1 };

          int tick           =  notes.front()->tick();
          qreal sp           = staff->spatium(tick);
          qreal stepDistance = sp * staff->lineDistance(tick) * .5;
          int stepOffset     = staff->staffType(tick)->stepOffset();

          qreal lx           = 10000.0;  // leftmost notehead position
          qreal upDotPosX    = 0.0;
          qreal downDotPosX  = 0.0;

          int nNotes = notes.size();
          int nAcc = 0;
          for (int i = nNotes-1; i >= 0; --i) {
                Note* note     = notes[i];
                Accidental* ac = note->accidental();
                if (ac && !note->fixed()) {
                      ac->layout();
                      AcEl acel;
                      acel.note   = note;
                      int line    = note->line();
                      acel.line   = line;
                      acel.x      = 0.0;
                      acel.top    = line * 0.5 * sp + ac->bbox().top();
                      acel.bottom = line * 0.5 * sp + ac->bbox().bottom();
                      acel.width  = ac->width();
                      QPointF bboxNE = ac->symBbox(ac->symbol()).topRight();
                      QPointF bboxSW = ac->symBbox(ac->symbol()).bottomLeft();
                      QPointF cutOutNE = ac->symCutOutNE(ac->symbol());
                      QPointF cutOutSW = ac->symCutOutSW(ac->symbol());
                      if (!cutOutNE.isNull()) {
                            acel.ascent     = cutOutNE.y() - bboxNE.y();
                            acel.rightClear = bboxNE.x() - cutOutNE.x();
                            }
                      else {
                            acel.ascent     = 0.0;
                            acel.rightClear = 0.0;
                            }
                      if (!cutOutSW.isNull()) {
                            acel.descent   = bboxSW.y() - cutOutSW.y();
                            acel.leftClear = cutOutSW.x() - bboxSW.x();
                            }
                      else {
                            acel.descent   = 0.0;
                            acel.leftClear = 0.0;
                            }
                      int pitchClass = (line + 700) % 7;
                      acel.next = columnBottom[pitchClass];
                      columnBottom[pitchClass] = nAcc;
                      aclist.append(acel);
                      ++nAcc;
                      }

                qreal hw     = note->headWidth();   // actual head width, including note & chord mag
                Chord* chord = note->chord();
                bool _up     = chord->up();
                qreal stemX  = chord->stemPosX();   // stem position for nominal notehead, but allowing for mag

                qreal overlapMirror;
                Stem* stem = chord->stem();
                if (stem)
                      overlapMirror = stem->lineWidth();
                else if (chord->durationType().headType() == NoteHead::Type::HEAD_WHOLE)
                      overlapMirror = styleP(StyleIdx::stemWidth) * chord->mag();
                else
                      overlapMirror = 0.0;

                qreal x;
                if (note->mirror()) {
                      if (_up)
                            x = stemX - overlapMirror;
                      else
                            x = stemX - hw + overlapMirror;
                      }
                else {
                      if (_up)
                            x = stemX - hw;
                      else
                            x = 0.0;
                      }

                note->rypos()  = (note->line() + stepOffset) * stepDistance;
                note->rxpos()  = x;
                // we need to do this now
                // or else note pos / readPos / userOff will be out of sync
                // and we rely on note->x() throughout the layout process
                note->adjustReadPos();

                // find leftmost non-mirrored note to set as X origin for accidental layout
                // a mirrored note that extends to left of segment X origin
                // will displace accidentals only if there is conflict
                qreal sx = x + chord->x(); // segment-relative X position of note
                if (note->mirror() && !chord->up() && sx < 0.0)
                      leftNotes.append(note);
                else if (sx < lx)
                      lx = sx;

                qreal xx = x + hw + chord->pos().x();

                Direction dotPosition = note->userDotPosition();
                if (chord->dots()) {
                      if (chord->up())
                            upDotPosX = qMax(upDotPosX, xx);
                      else
                            downDotPosX = qMax(downDotPosX, xx);

                      if (dotPosition == Direction::AUTO && nNotes > 1 && note->visible() && !note->dotsHidden()) {
                            // resolve dot conflicts
                            int line = note->line();
                            Note* above = (i < nNotes - 1) ? notes[i+1] : 0;
                            if (above && (!above->visible() || above->dotsHidden()))
                                  above = 0;
                            int intervalAbove = above ? line - above->line() : 1000;
                            Note* below = (i > 0) ? notes[i-1] : 0;
                            if (below && (!below->visible() || below->dotsHidden()))
                                  below = 0;
                            int intervalBelow = below ? below->line() - line : 1000;
                            if ((line & 1) == 0) {
                                  // line
                                  if (intervalAbove == 1 && intervalBelow != 1)
                                        dotPosition = Direction::DOWN;
                                  else if (intervalBelow == 1 && intervalAbove != 1)
                                        dotPosition = Direction::UP;
                                  else if (intervalAbove == 0 && above->chord()->dots()) {
                                        // unison
                                        if (((above->voice() & 1) == (note->voice() & 1))) {
                                              above->setDotY(Direction::UP);
                                              dotPosition = Direction::DOWN;
                                              }
                                        }
                                  }
                            else {
                                  // space
                                  if (intervalAbove == 0 && above->chord()->dots()) {
                                        // unison
                                        if (!(note->voice() & 1))
                                              dotPosition = Direction::UP;
                                        else {
                                              if (!(above->voice() & 1))
                                                    above->setDotY(Direction::UP);
                                              else
                                                    dotPosition = Direction::DOWN;
                                              }
                                        }
                                  }
                            }
                      }
                note->setDotY(dotPosition);  // also removes invalid dots
                }

          if (segment) {
                // align all dots for segment/staff
                // it would be possible to dots for up & down chords separately
                // this would require space to have been allocated previously
                // when calculating chord offsets
                segment->setDotPosX(staff->idx(), qMax(upDotPosX, downDotPosX));
                }

          if (nAcc == 0)
                return;

          QVector<int> umi;
          qreal pd  = styleP(StyleIdx::accidentalDistance);
          qreal pnd = styleP(StyleIdx::accidentalNoteDistance);
          qreal colOffset = 0.0;

          if (nAcc >= 2 && aclist[nAcc-1].line - aclist[0].line >= 7) {

                // accidentals spread over an octave or more
                // set up columns for accidentals with octave matches
                // these will start at right and work to the left
                // unmatched accidentals will use zig zag approach (see below)
                // starting to the left of the octave columns

                qreal minX = 0.0;
                int columnTop[7] = { -1, -1, -1, -1, -1, -1, -1 };

                // find columns of octaves
                for (int pc = 0; pc < 7; ++pc) {
                      if (columnBottom[pc] == -1)
                            continue;
                      // calculate column height
                      for (int j = columnBottom[pc]; j != -1; j = aclist[j].next)
                            columnTop[pc] = j;
                      }

                // compute reasonable column order
                // use zig zag
                QVector<int> column;
                QVector<int> unmatched;
                int n = nAcc - 1;
                for (int i = 0; i <= n; ++i, --n) {
                      int pc = (aclist[i].line + 700) % 7;
                      if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
                            if (!column.contains(pc))
                                  column.append(pc);
                            }
                      else
                            unmatched.append(i);
                      if (i == n)
                            break;
                      pc = (aclist[n].line + 700) % 7;
                      if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
                            if (!column.contains(pc))
                                  column.append(pc);
                            }
                      else
                            unmatched.append(n);
                      }
                int nColumns = column.size();
                int nUnmatched = unmatched.size();

                // handle unmatched accidentals
                for (int i = 0; i < nUnmatched; ++i) {
                      // first try to slot it into an existing column
                      AcEl* me = &aclist[unmatched[i]];
                      // find column
                      bool found = false;
                      for (int j = 0; j < nColumns; ++j) {
                            int pc = column[j];
                            int above = -1;
                            int below = -1;
                            // find slot within column
                            for (int k = columnBottom[pc]; k != -1; k = aclist[k].next) {
                                  if (aclist[k].line < me->line) {
                                        above = k;
                                        break;
                                        }
                                  below = k;
                                  }
                            // check to see if accidental can fit in slot
                            qreal myPd = pd * me->note->accidental()->mag();
                            bool conflict = false;
                            if (above != -1 && me->top - aclist[above].bottom < myPd)
                                  conflict = true;
                            else if (below != -1 && aclist[below].top - me->bottom < myPd)
                                  conflict = true;
                            if (!conflict) {
                                  // insert into column
                                  found = true;
                                  me->next = above;
                                  if (above == -1)
                                        columnTop[pc] = unmatched[i];
                                  if (below != -1)
                                        aclist[below].next = unmatched[i];
                                  else
                                        columnBottom[pc] = unmatched[i];
                                  break;
                                  }
                            }
                      // if no slot found, then add to list of unmatched accidental indices
                      if (!found)
                            umi.push_back(unmatched[i]);
                      }
                nAcc = umi.size();
                if (nAcc > 1)
                      qSort(umi);

                // lay out columns
                for (int i = 0; i < nColumns; ++i) {
                      int pc = column[i];
                      AcEl* below = 0;
                      // lay out accidentals
                      for (int j = columnBottom[pc]; j != -1; j = aclist[j].next) {
                            qreal x = layoutAccidental(&aclist[j], 0, below, colOffset, leftNotes, pnd, pd, sp);
                            minX = qMin(minX, x);
                            below = &aclist[j];
                            }
                      // align within column
                      int next = -1;
                      for (int j = columnBottom[pc]; j != -1; j = next) {
                            next = aclist[j].next;
                            if (next != -1 && aclist[j].line == aclist[next].line)
                                  continue;
                            aclist[j].x = minX;
                            }
                      // move to next column
                      colOffset = minX;
                      }

                }

          else {
                for (int i = 0; i < nAcc; ++i)
                      umi.push_back(i);
                }

          if (nAcc) {

                // for accidentals with no octave matches, use zig zag approach
                // layout right to left in pairs, (next) highest then lowest

                AcEl* me = &aclist[umi[0]];
                AcEl* above = 0;
                AcEl* below = 0;

                // layout top accidental
                layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);

                // layout bottom accidental
                int n = nAcc - 1;
                if (n > 0) {
                      above = me;
                      me = &aclist[umi[n]];
                      layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
                      }

                // layout middle accidentals
                if (n > 1) {
                      for (int i = 1; i < n; ++i, --n) {
                            // next highest
                            below = me;
                            me = &aclist[umi[i]];
                            layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
                            if (i == n - 1)
                                  break;
                            // next lowest
                            above = me;
                            me = &aclist[umi[n-1]];
                            layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
                            }
                      }

                }

          for (const AcEl& e : aclist) {
                // even though we initially calculate accidental position relative to segment
                // we must record pos for accidental relative to note,
                // since pos is always interpreted relative to parent
                Note* note = e.note;
                qreal x    = e.x + lx - (note->x() + note->chord()->x());
                note->accidental()->setPos(x, 0);
                note->accidental()->adjustReadPos();
                }
          }

    #define beamModeMid(a) (a == Beam::Mode::MID || a == Beam::Mode::BEGIN32 || a == Beam::Mode::BEGIN64)

    //---------------------------------------------------------
    //   beamGraceNotes
    //---------------------------------------------------------

    void Score::beamGraceNotes(Chord* mainNote, bool after)
          {
          ChordRest* a1    = 0;      // start of (potential) beam
          Beam* beam       = 0;      // current beam
          Beam::Mode bm = Beam::Mode::AUTO;
          QVector<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();

          for (ChordRest* cr : graceNotes) {
                bm = Groups::endBeam(cr);
                if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
                      if (beam) {
                            beam->layoutGraceNotes();
                            beam = 0;
                            }
                      if (a1) {
                            a1->removeDeleteBeam(false);
                            a1 = 0;
                            }
                      cr->removeDeleteBeam(false);
                      continue;
                      }
                if (beam) {
                      bool beamEnd = bm == Beam::Mode::BEGIN;
                      if (!beamEnd) {
                            cr->removeDeleteBeam(true);
                            beam->add(cr);
                            cr = 0;
                            beamEnd = (bm == Beam::Mode::END);
                            }
                      if (beamEnd) {
                            beam->layoutGraceNotes();
                            beam = 0;
                            }
                      }
                if (!cr)
                      continue;
                if (a1 == 0)
                      a1 = cr;
                else {
                      if (!beamModeMid(bm) && (bm == Beam::Mode::BEGIN)) {
                            a1->removeDeleteBeam(false);
                            a1 = cr;
                            }
                      else {
                            beam = a1->beam();
                            if (beam == 0 || beam->elements().front() != a1) {
                                  beam = new Beam(this);
                                  beam->setGenerated(true);
                                  beam->setTrack(mainNote->track());
                                  a1->removeDeleteBeam(true);
                                  beam->add(a1);
                                  }
                            cr->removeDeleteBeam(true);
                            beam->add(cr);
                            a1 = 0;
                            }
                      }
                }
          if (beam)
                beam->layoutGraceNotes();
          else if (a1)
                a1->removeDeleteBeam(false);
          }

    //---------------------------------------------------------
    //   layoutSpanner
    //    called after dragging a staff
    //---------------------------------------------------------

    void Score::layoutSpanner()
          {
          int tracks = ntracks();
          for (int track = 0; track < tracks; ++track) {
                for (Segment* segment = firstSegment(SegmentType::All); segment; segment = segment->next1()) {
                      if (track == tracks-1) {
                            int n = segment->annotations().size();
                            for (int i = 0; i < n; ++i)
                                  segment->annotations().at(i)->layout();
                            }
                      Element* e = segment->element(track);
                      if (e && e->isChord()) {
                            Chord* c = toChord(segment->element(track));
                            c->layoutStem();
                            for (Note* n : c->notes()) {
                                  Tie* tie = n->tieFor();
                                  if (tie)
                                        tie->layout();
                                  for (Spanner* sp : n->spannerFor())
                                        sp->layout();
                                  }
                            }
                      }
                }
          rebuildBspTree();
          }

    //---------------------------------------------------------
    //   hideEmptyStaves
    //---------------------------------------------------------

    void Score::hideEmptyStaves(System* system, bool isFirstSystem)
          {
          int staves   = _staves.size();
          int staffIdx = 0;
          bool systemIsEmpty = true;

          for (Staff* staff : _staves) {
                SysStaff* ss  = system->staff(staffIdx);
    //            bool oldShow = ss->show();

                Staff::HideMode hideMode = staff->hideWhenEmpty();

                if (hideMode == Staff::HideMode::ALWAYS
                    || (styleB(StyleIdx::hideEmptyStaves)
                        && (staves > 1)
                        && !(isFirstSystem && styleB(StyleIdx::dontHideStavesInFirstSystem))
                        && hideMode != Staff::HideMode::NEVER)) {
                      bool hideStaff = true;
                      for (MeasureBase* m : system->measures()) {
                            if (!m->isMeasure())
                                  continue;
                            Measure* measure = toMeasure(m);
                            if (!measure->isMeasureRest(staffIdx)) {
                                  hideStaff = false;
                                  break;
                                  }
                            }
                      // check if notes moved into this staff
                      Part* part = staff->part();
                      int n = part->nstaves();
                      if (hideStaff && (n > 1)) {
                            int idx = part->staves()->front()->idx();
                            for (int i = 0; i < part->nstaves(); ++i) {
                                  int st = idx + i;

                                  foreach (MeasureBase* mb, system->measures()) {
                                        if (!mb->isMeasure())
                                              continue;
                                        Measure* m = toMeasure(mb);
                                        for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
                                              for (int voice = 0; voice < VOICES; ++voice) {
                                                    ChordRest* cr = s->cr(st * VOICES + voice);
                                                    if (cr == 0 || cr->isRest())
                                                          continue;
                                                    int staffMove = cr->staffMove();
                                                    if (staffIdx == st + staffMove) {
                                                          hideStaff = false;
                                                          break;
                                                          }
                                                    }
                                              }
                                        if (!hideStaff)
                                              break;
                                        }
                                  if (!hideStaff)
                                        break;
                                  }
                            }
                      ss->setShow(hideStaff ? false : staff->show());
                      if (ss->show())
                            systemIsEmpty = false;
                      }
                else {
                      systemIsEmpty = false;
                      ss->setShow(true);
                      }

                ++staffIdx;
                }
          if (systemIsEmpty) {
                for (Staff* staff : _staves) {
                      SysStaff* ss  = system->staff(staff->idx());
                      if (staff->showIfEmpty() && !ss->show()) {
                            ss->setShow(true);
                            systemIsEmpty = false;
                            }
                      }
                }
          // dont allow a complete empty system
          if (systemIsEmpty) {
                Staff* staff = _staves.front();
                SysStaff* ss = system->staff(staff->idx());
                ss->setShow(true);
                }
          }

    //---------------------------------------------------------
    //   connectTies
    ///   Rebuild tie connections.
    //---------------------------------------------------------

    void Score::connectTies(bool silent)
          {
          int tracks = nstaves() * VOICES;
          Measure* m = firstMeasure();
          if (!m)
                return;
          SegmentType st = SegmentType::ChordRest;
          for (Segment* s = m->first(st); s; s = s->next1(st)) {
                for (int i = 0; i < tracks; ++i) {
                      Element* e = s->element(i);
                      if (e == 0 || !e->isChord())
                            continue;
                      Chord* c = toChord(e);
                      for (Note* n : c->notes()) {
                            // connect a tie without end note
                            Tie* tie = n->tieFor();
                            if (tie && !tie->endNote()) {
                                  Note* nnote;
                                  if (_mscVersion <= 114)
                                        nnote = searchTieNote114(n);
                                  else
                                        nnote = searchTieNote(n);
                                  if (nnote == 0) {
                                        if (!silent) {
                                              qDebug("next note at %d track %d for tie not found (version %d)", s->tick(), i, _mscVersion);
                                              delete tie;
                                              n->setTieFor(0);
                                              }
                                        }
                                  else {
                                        tie->setEndNote(nnote);
                                        nnote->setTieBack(tie);
                                        }
                                  }
                            // connect a glissando without initial note (old glissando format)
                            for (Spanner* spanner : n->spannerBack()) {
                                  if (spanner->isGlissando() && !spanner->startElement()) {
                                        Note* initialNote = Glissando::guessInitialNote(n->chord());
                                        n->removeSpannerBack(spanner);
                                        if (initialNote) {
                                              spanner->setStartElement(initialNote);
                                              spanner->setEndElement(n);
                                              spanner->setTick(initialNote->chord()->tick());
                                              spanner->setTick2(n->chord()->tick());
                                              spanner->setTrack(n->track());
                                              spanner->setTrack2(n->track());
                                              spanner->setParent(initialNote);
                                              initialNote->add(spanner);
                                              }
                                        else {
                                              delete spanner;
                                              }
                                        }
                                  }
                            // spanner with no end element can happen during copy/paste
                            for (Spanner* spanner : n->spannerFor()) {
                                  if (spanner->endElement() == nullptr) {
                                        n->removeSpannerFor(spanner);
                                        delete spanner;
                                        }
                                  }
                            }
                      // connect two note tremolos
                      Tremolo* tremolo = c->tremolo();
                      if (tremolo && tremolo->twoNotes() && !tremolo->chord2()) {
                            for (Segment* ls = s->next1(st); ls; ls = ls->next1(st)) {
                                  Chord* nc = toChord(ls->element(i));
                                  if (nc == 0)
                                        continue;
                                  if (!nc->isChord())
                                        qDebug("cannot connect tremolo");
                                  else {
                                        nc->setTremolo(tremolo);
                                        tremolo->setChords(c, nc);
                                        // cross-measure tremolos are not supported
                                        // but can accidentally result from copy & paste
                                        // remove them now
                                        if (c->measure() != nc->measure())
                                              c->remove(tremolo);
                                        }
                                  break;
                                  }
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   checkDivider
    //---------------------------------------------------------

    static void checkDivider(bool left, System* s, qreal yOffset)
          {
          SystemDivider* divider = left ? s->systemDividerLeft() : s->systemDividerRight();
          if (s->score()->styleB(left ? StyleIdx::dividerLeft : StyleIdx::dividerRight)) {
                if (!divider) {
                      divider = new SystemDivider(s->score());
                      divider->setDividerType(left ? SystemDivider::Type::LEFT : SystemDivider::Type::RIGHT);
                      divider->setGenerated(true);
                      s->add(divider);
                      }
                divider->layout();
                divider->rypos() = divider->height() * .5 + yOffset;
                if (left) {
                      divider->rypos() += s->score()->styleD(StyleIdx::dividerLeftY) * SPATIUM20;
                      divider->rxpos() =  s->score()->styleD(StyleIdx::dividerLeftX) * SPATIUM20;
                      }
                else {
                      divider->rypos() += s->score()->styleD(StyleIdx::dividerRightY) * SPATIUM20;
                      divider->rxpos() =  s->score()->styleD(StyleIdx::pagePrintableWidth) * DPI - divider->width();
                      divider->rxpos() += s->score()->styleD(StyleIdx::dividerRightX) * SPATIUM20;
                      }
                divider->adjustReadPos();
                }
          else if (divider) {
                if (divider->generated()) {
                      s->remove(divider);
                      delete divider;
                      }
                else
                      s->score()->undoRemoveElement(divider);
                }
          }

    //---------------------------------------------------------
    //   layoutPage
    //    restHeight - vertical space which has to be distributed
    //                 between systems
    //    The algorithm tries to produce most equally spaced
    //    systems.
    //---------------------------------------------------------

    static void layoutPage(Page* page, qreal restHeight)
          {
          Score* score  = page->score();
          int nsystems  = page->systems().size() - 1;

          QList<System*> sList;

          for (int i = 0; i < nsystems; ++i) {
                System* s1 = page->systems().at(i);
                System* s2 = page->systems().at(i+1);
                s1->setDistance(s2->y() - s1->y());
                if (s1->vbox() || s2->vbox() || s1->hasFixedDownDistance())
                      continue;
                sList.push_back(s1);
                }

          if (sList.empty() || MScore::noVerticalStretch || score->layoutMode() == LayoutMode::SYSTEM) {
                if (score->layoutMode() == LayoutMode::FLOAT) {
                      qreal y = restHeight * .5;
                      for (System* system : page->systems())
                            system->move(QPointF(0.0, y));
                      }
                // remove system dividers
                for (System* s : page->systems()) {
                      SystemDivider* sd = s->systemDividerLeft();
                      if (sd) {
                            s->remove(sd);
                            delete sd;
                            }
                      sd = s->systemDividerRight();
                      if (sd) {
                            s->remove(sd);
                            delete sd;
                            }
                      }
                return;
                }

          std::sort(sList.begin(), sList.end(), [](System* a, System* b) { return a->distance() < b->distance(); });

          qreal maxDist = score->styleP(StyleIdx::maxSystemDistance);

          qreal dist = sList[0]->distance();
          for (int i = 1; i < sList.size(); ++i) {
                qreal ndist = sList[i]->distance();
                qreal fill = ndist - dist;
                dist       = ndist;
                if (fill > 0.0) {
                      qreal totalFill = fill * i;
                      if (totalFill > restHeight) {
                            totalFill = restHeight;
                            fill = restHeight / i;
                            }
                      for (int k = 0; k < i; ++k) {
                            System* s = sList[k];
                            qreal d = s->distance() + fill;
                            if ((d - s->height()) > maxDist)
                                  d = qMax(maxDist + s->height(), s->distance());
                            s->setDistance(d);
                            }
                      restHeight -= totalFill;
                      if (restHeight <= 0)
                            break;
                      }
                }
          if (restHeight > 0.0) {
                qreal fill = restHeight / sList.size();
                for (int i = 0; i < sList.size(); ++i) {
                      System* s = sList[i];
                      qreal d = s->distance() + fill;
                      if ((d - s->height()) > maxDist)
                            d = qMax(maxDist + s->height(), s->distance());
                      s->setDistance(d);
                      }
                }

          qreal y = page->systems().at(0)->y();
          for (int i = 0; i < nsystems; ++i) {
                System* s1  = page->systems().at(i);
                System* s2  = page->systems().at(i+1);
                s1->rypos() = y;
                y          += s1->distance();

                if (!(s1->vbox() || s2->vbox() || s1->hasFixedDownDistance())) {
                      qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
                      checkDivider(true,  s1, yOffset);
                      checkDivider(false, s1, yOffset);
                      }
                }
          page->systems().back()->rypos() = y;
          }

    //---------------------------------------------------------
    //   Spring
    //---------------------------------------------------------

    struct Spring {
          int seg;
          qreal stretch;
          qreal fix;
          Spring(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
          };

    typedef std::multimap<qreal, Spring, std::less<qreal> > SpringMap;

    //---------------------------------------------------------
    //   sff2
    //    compute 1/Force for a given Extend
    //---------------------------------------------------------

    static qreal sff2(qreal width, qreal xMin, const SpringMap& springs)
          {
          if (width <= xMin)
                return 0.0;
          auto i = springs.begin();
          qreal c  = i->second.stretch;
          if (c == 0.0)           //DEBUG
                c = 1.1;
          qreal f = 0.0;
          for (; i != springs.end();) {
                xMin -= i->second.fix;
                f = (width - xMin) / c;
                ++i;
                if (i == springs.end() || f <= i->first)
                      break;
                c += i->second.stretch;
                }
          return f;
          }

    //---------------------------------------------------------
    //   respace
    //---------------------------------------------------------

    void Score::respace(std::vector<ChordRest*>* elements)
          {
          ChordRest* cr1 = elements->front();
          ChordRest* cr2 = elements->back();
          int n          = elements->size();
          qreal x1       = cr1->segment()->pos().x();
          qreal x2       = cr2->segment()->pos().x();

          qreal width[n-1];
          int ticksList[n-1];
          int minTick = 100000;

          for (int i = 0; i < n-1; ++i) {
                ChordRest* cr  = (*elements)[i];
                ChordRest* ncr  = (*elements)[i+1];
                width[i]       = cr->shape().minHorizontalDistance(ncr->shape());
                ticksList[i]   = cr->duration().ticks();
                minTick = qMin(ticksList[i], minTick);
                }

          //---------------------------------------------------
          // compute stretches
          //---------------------------------------------------

          SpringMap springs;
          qreal minimum = 0.0;
          for (int i = 0; i < n-1; ++i) {
                qreal w   = width[i];
                int t     = ticksList[i];
                qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
                qreal d   = w / str;

                springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
                minimum += w;
                }

          //---------------------------------------------------
          //    distribute stretch to elements
          //---------------------------------------------------

          qreal force = sff2(x2 - x1, minimum, springs);
          for (auto i = springs.begin(); i != springs.end(); ++i) {
                qreal stretch = force * i->second.stretch;
                if (stretch < i->second.fix)
                      stretch = i->second.fix;
                width[i->second.seg] = stretch;
                }
          qreal x = x1;
          for (int i = 1; i < n-1; ++i) {
                x += width[i-1];
                ChordRest* cr = (*elements)[i];
                qreal dx = x - cr->segment()->pos().x();
                cr->rxpos() += dx;
                }
          }

    //---------------------------------------------------------
    //   getEmptyPage
    //---------------------------------------------------------

    void LayoutContext::getEmptyPage()
          {
          if (curPage >= score->npages()) {
                Page* page = new Page(score);
                page->setNo(score->pages().size());
                score->pages().push_back(page);
                }
          else {
                page = score->pages()[curPage];
                }
          page->setNo(curPage);
          page->layout();
          qreal x, y;
          if (MScore::verticalOrientation()) {
                x = 0.0;
                y = (curPage == 0) ? 0.0 : score->pages()[curPage - 1]->pos().y() + page->height() + MScore::verticalPageGap;
                }
          else {
                y = 0.0;
                x = (curPage == 0) ? 0.0 : score->pages()[curPage - 1]->pos().x()
                   + page->width()
                   + (((curPage+score->pageNumberOffset()) & 1) ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven);
                }
          ++curPage;
          page->setPos(x, y);
          page->systems().clear();
          }

    //---------------------------------------------------------
    //   getNextSystem
    //---------------------------------------------------------

    System* Score::getNextSystem(LayoutContext& lc)
          {
          bool isVBox = lc.curMeasure->isVBox();
          System* system;
          if (lc.systemList.empty()) {
                system = new System(this);
                lc.systemOldMeasure = 0;
                }
          else {
                system = lc.systemList.takeFirst();
                lc.systemOldMeasure = system->measures().empty() ? 0 : system->measures().back();
                system->clear();   // remove measures from system
                }
          _systems.append(system);
          if (!isVBox) {
                int nstaves = Score::nstaves();
                for (int i = system->staves()->size(); i < nstaves; ++i)
                      system->insertStaff(i);
                int dn = system->staves()->size() - nstaves;
                for (int i = 0; i < dn; ++i)
                      system->removeStaff(system->staves()->size()-1);
                }
          return system;
          }

    //---------------------------------------------------------
    //   createMMRest
    //    create a multi measure rest from m to lm (inclusive)
    //---------------------------------------------------------

    void Score::createMMRest(Measure* m, Measure* lm, const Fraction& len)
          {
          int n = 1;
          for (Measure* mm = m->nextMeasure(); mm; mm = mm->nextMeasure()) {
                ++n;
                mm->setMMRestCount(-1);
                if (mm->mmRest())
                      undo(new ChangeMMRest(mm, 0));
                if (mm == lm)
                      break;
                }
          Measure* mmr = m->mmRest();
          if (mmr) {
                if (mmr->len() != len) {
                      Segment* s = mmr->findSegmentR(SegmentType::EndBarLine, mmr->ticks());
                      mmr->setLen(len);
                      if (s)
                            s->setTick(mmr->endTick());
                      }
                }
          else {
                mmr = new Measure(this);
                mmr->setLen(len);
                mmr->setTick(m->tick());
                mmr->setPageBreak(lm->pageBreak());
                mmr->setLineBreak(lm->lineBreak());
                undo(new ChangeMMRest(m, mmr));
                }
          mmr->setMMRestCount(n);
          mmr->setNo(m->no());

          Segment* ss = lm->findSegmentR(SegmentType::EndBarLine, lm->ticks());
          if (ss) {
                Segment* ds = mmr->undoGetSegment(SegmentType::EndBarLine, lm->endTick());
                for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
                      Element* e = ss->element(staffIdx * VOICES);
                      if (e) {
                            if (!ds->element(staffIdx * VOICES)) {
                                  Element* ee = e->clone();
                                  ee->setParent(ds);
                                  undoAddElement(ee);
                                  }
                            else {
                                  BarLine* bd = toBarLine(ds->element(staffIdx * VOICES));
                                  BarLine* bs = toBarLine(e);
                                  if (bd->barLineType() != bs->barLineType()) {
                                        undoChangeProperty(bd, P_ID::BARLINE_TYPE, QVariant::fromValue(bs->barLineType()));
                                        undoChangeProperty(bd, P_ID::GENERATED, true);
                                        }
                                  }
                            }
                      }
                }

          mmr->setRepeatStart(m->repeatStart() || lm->repeatStart());
          mmr->setRepeatEnd(m->repeatEnd() || lm->repeatEnd());

          ElementList oldList = mmr->takeElements();
          ElementList newList = lm->el();

          for (Element* e : m->el()) {
                if (e->isMarker())
                      newList.push_back(e);
                }
          for (Element* e : newList) {
                bool found = false;
                for (Element* ee : oldList) {
                      if (ee->type() == e->type()) {
                            mmr->add(ee);
                            auto i = std::find(oldList.begin(), oldList.end(), ee);
                            if (i != oldList.end())
                                  oldList.erase(i);
                            found = true;
                            break;
                            }
                      }
                if (!found)
                      mmr->add(e->clone());
                }
          for (Element* e : oldList)
                delete e;
          Segment* s = mmr->undoGetSegmentR(SegmentType::ChordRest, 0);
          for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                int track = staffIdx * VOICES;
                if (s->element(track) == 0) {
                      Rest* r = new Rest(this);
                      r->setDurationType(TDuration::DurationType::V_MEASURE);
                      r->setDuration(mmr->len());
                      r->setTrack(track);
                      r->setParent(s);
                      undo(new AddElement(r));
                      }
                }

          //
          // check for clefs
          //
          Segment* cs = lm->findSegmentR(SegmentType::Clef, lm->ticks());
          Segment* ns = mmr->findSegment(SegmentType::Clef, lm->endTick());
          if (cs) {
                if (ns == 0)
                      ns = mmr->undoGetSegmentR(SegmentType::Clef, lm->ticks());
                for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                      int track = staffIdx * VOICES;
                      Clef* clef = toClef(cs->element(track));
                      if (clef) {
                            if (ns->element(track) == 0)
                                  ns->add(clef->clone());
                            else {
                                  //TODO: check if same clef
                                  }
                            }
                      }
                }
          else if (ns)
                undo(new RemoveElement(ns));

          //
          // check for time signature
          //
          cs = m->findSegmentR(SegmentType::TimeSig, 0);
          ns = mmr->findSegment(SegmentType::TimeSig, m->tick());
          if (cs) {
                if (ns == 0)
                      ns = mmr->undoGetSegmentR(SegmentType::TimeSig, 0);
                for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                      int track = staffIdx * VOICES;
                      TimeSig* ts = toTimeSig(cs->element(track));
                      if (ts) {
                            TimeSig* nts = toTimeSig(ns->element(track));
                            if (!nts) {
                                  nts = ts->clone();
                                  nts->setParent(ns);
                                  undo(new AddElement(nts));
                                  }
                            else {
                                  nts->setSig(ts->sig(), ts->timeSigType());
                                  nts->layout();
                                  }
                            }
                      }
                }
          else if (ns)
                undo(new RemoveElement(ns));

          //
          // check for ambitus
          //
          cs = m->findSegmentR(SegmentType::Ambitus, 0);
          ns = mmr->findSegment(SegmentType::Ambitus, m->tick());
          if (cs) {
                if (ns == 0)
                      ns = mmr->undoGetSegmentR(SegmentType::Ambitus, 0);
                for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                      int track = staffIdx * VOICES;
                      Ambitus* a = toAmbitus(cs->element(track));
                      if (a) {
                            Ambitus* na = toAmbitus(ns->element(track));
                            if (!na) {
                                  na = a->clone();
                                  na->setParent(ns);
                                  undo(new AddElement(na));
                                  }
                            else {
                                  na->initFrom(a);
                                  na->layout();
                                  }
                            }
                      }
                }
          else if (ns)
                undo(new RemoveElement(ns));

          //
          // check for key signature
          //
          cs = m->findSegmentR(SegmentType::KeySig, 0);
          ns = mmr->findSegmentR(SegmentType::KeySig, 0);
          if (cs) {
                if (ns == 0)
                      ns = mmr->undoGetSegmentR(SegmentType::KeySig, 0);
                for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                      int track = staffIdx * VOICES;
                      KeySig* ts  = toKeySig(cs->element(track));
                      KeySig* nts = toKeySig(ns->element(track));
                      if (ts) {
                            if (!nts) {
                                  KeySig* nks = ts->clone();
                                  nks->setParent(ns);
                                  undo(new AddElement(nks));
                                  }
                            else {
                                  if (!(nts->keySigEvent() == ts->keySigEvent())) {
                                        undo(new ChangeKeySig(nts, ts->keySigEvent(), nts->showCourtesy()));
                                        }
                                  }
                            }
                      }
                }
          else if (ns && ns->empty())
                undo(new RemoveElement(ns));

          //
          // check for rehearsal mark etc.
          //
          cs = m->findSegmentR(SegmentType::ChordRest, 0);
          if (cs) {
                for (Element* e : cs->annotations()) {
                      if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText()))
                            continue;

                      bool found = false;
                      for (Element* ee : s->annotations()) {
                            if (ee->type() == e->type() && ee->track() == e->track()) {
                                  found = true;
                                  break;
                                  }
                            }
                      if (!found) {
                            Element* ne = e->linkedClone();
                            ne->setParent(s);
                            undo(new AddElement(ne));
                            }
                      }
                }

          for (Element* e : s->annotations()) {
                if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText()))
                      continue;
                bool found = false;
                for (Element* ee : cs->annotations()) {
                      if (ee->type() == e->type() && ee->track() == e->track()) {
                            found = true;
                            break;
                            }
                      }
                if (!found)
                      undo(new RemoveElement(e));
                }

          MeasureBase* nm = _showVBox ? lm->next() : lm->nextMeasure();
          mmr->setNext(nm);
          mmr->setPrev(m->prev());
          }

    //---------------------------------------------------------
    // validMMRestMeasure
    //    return true if this might be a measure in a
    //    multi measure rest
    //---------------------------------------------------------

    static bool validMMRestMeasure(Measure* m)
          {
          if (m->irregular())
                return false;

          int n = 0;
          for (Segment* s = m->first(); s; s = s->next()) {
                for (Element* e : s->annotations()) {
                      if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText()))
                            return false;
                      }
                if (s->isChordRestType()) {
                      bool restFound = false;
                      int tracks = m->score()->ntracks();
                      for (int track = 0; track < tracks; ++track) {
                            if ((track % VOICES) == 0 && !m->score()->staff(track/VOICES)->show()) {
                                  track += VOICES-1;
                                  continue;
                                  }
                            if (s->element(track))  {
                                  if (!s->element(track)->isRest())
                                        return false;
                                  Rest* rest = toRest(s->element(track));
                                  if (rest->articulations().size() > 0) // break on fermata
                                        return false;
                                  restFound = true;
                                  }
                            }
                      if (restFound)
                            ++n;
                      // measure is not empty if there is more than one rest
                      if (n > 1)
                            return false;
                      }
                }
          return true;
          }

    //---------------------------------------------------------
    //  breakMultiMeasureRest
    //    return true if this measure should start a new
    //    multi measure rest
    //---------------------------------------------------------

    static bool breakMultiMeasureRest(Measure* m)
          {
          if (m->breakMultiMeasureRest())
                return true;

          if (m->repeatStart()
             || (m->prevMeasure() && m->prevMeasure()->repeatEnd())
             || (m->prevMeasure() && (m->prevMeasure()->sectionBreak())))
                return true;

          auto sl = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
          for (auto i : sl) {
                Spanner* s = i.value;
                if (s->isVolta() && (s->tick() == m->tick() || s->tick2() == m->tick()))
                      return true;
                }

          // break for marker in this measure
          for (Element* e : m->el()) {
                if (e->isMarker()) {
                      Marker* mark = toMarker(e);
                      if (!(mark->align() & Align::RIGHT))
                            return true;
                      }
                }

          // break for marker & jump in previous measure
          Measure* pm = m->prevMeasure();
          if (pm) {
                for (Element* e : pm->el()) {
                      if (e->isJump())
                            return true;
                      else if (e->isMarker()) {
                            Marker* mark = toMarker(e);
                            if (mark->align() & Align::RIGHT)
                                  return true;
                            }
                      }
                }

          // break for end of volta
          auto l = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
          for (auto isp : l) {
                Spanner* s = isp.value;
                if (s->isVolta() && (s->tick2() == m->endTick()))
                      return true;
                }

          for (Segment* s = m->first(); s; s = s->next()) {
                for (Element* e : s->annotations()) {
                      if (e->isRehearsalMark() ||
                          e->isTempoText() ||
                          ((e->isHarmony() || e->isStaffText()) && (e->systemFlag() || m->score()->staff(e->staffIdx())->show())))
                            return true;
                      }
                for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
                      if (!m->score()->staff(staffIdx)->show())
                            continue;
                      Element* e = s->element(staffIdx * VOICES);
                      if (!e || e->generated())
                            continue;
                      if (s->isStartRepeatBarLineType())
                            return true;
                      if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && m->tick())
                            return true;
                      if (s->isClefType()) {
                            if (s->tick() != m->endTick() && m->tick())
                                  return true;
                            }
                      }
                }
          if (pm) {
                Segment* s = pm->findSegmentR(SegmentType::EndBarLine, pm->ticks());
                if (s) {
                      for (int staffIdx = 0; staffIdx < s->score()->nstaves(); ++staffIdx) {
                            BarLine* bl = toBarLine(s->element(staffIdx * VOICES));
                            if (bl) {
                                  BarLineType t = bl->barLineType();
                                  if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated())
                                        return true;
                                  else
                                        break;
                                  }
                            }
                      }
                if (pm->findSegment(SegmentType::Clef, m->tick()))
                      return true;
                }
          return false;
          }

    //---------------------------------------------------------
    //   adjustMeasureNo
    //---------------------------------------------------------

    int LayoutContext::adjustMeasureNo(MeasureBase* m)
          {
          measureNo += m->noOffset();
          m->setNo(measureNo);
          if (!m->irregular())          // dont count measure
                ++measureNo;
          if (m->sectionBreak())
                measureNo = 0;
          return measureNo;
          }

    //---------------------------------------------------------
    //   createBeams
    //    helper function
    //---------------------------------------------------------

    void Score::createBeams(Measure* measure)
          {
          bool crossMeasure = styleB(StyleIdx::crossMeasureValues);

          for (int track = 0; track < ntracks(); ++track) {
                Staff* stf = staff(track2staff(track));

                // dont compute beams for invisible staffs and tablature without stems
                if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->slashStyle()))
                      continue;

                ChordRest* a1    = 0;      // start of (potential) beam
                Beam* beam       = 0;      // current beam
                Beam::Mode bm    = Beam::Mode::AUTO;
                ChordRest* prev  = 0;
                bool checkBeats  = false;
                Fraction stretch = 1;
                QHash<int, TDuration> beatSubdivision;

                // if this measure is simple meter (actually X/4),
                // then perform a prepass to determine the subdivision of each beat

                beatSubdivision.clear();
                TimeSig* ts = stf->timeSig(measure->tick());
                checkBeats  = false;
                stretch     = ts ? ts->stretch() : 1;

                const SegmentType st = SegmentType::ChordRest;
                if (ts && ts->denominator() == 4) {
                      checkBeats = true;
                      for (Segment* s = measure->first(st); s; s = s->next(st)) {
                            ChordRest* mcr = toChordRest(s->element(track));
                            if (mcr == 0)
                                  continue;
                            int beat = ((mcr->rtick() * stretch.numerator()) / stretch.denominator()) / MScore::division;
                            if (beatSubdivision.contains(beat))
                                  beatSubdivision[beat] = qMin(beatSubdivision[beat], mcr->durationType());
                            else
                                  beatSubdivision[beat] = mcr->durationType();
                            }
                      }

                for (Segment* segment = measure->first(st); segment; segment = segment->next(st)) {
                      ChordRest* cr = segment->cr(track);
                      if (cr == 0)
                            continue;
                      for (Lyrics* l : cr->lyrics()) {
                            if (l)
                                  l->layout();
                            }

                      // handle grace notes and cross-measure beaming
                      if (cr->isChord()) {
                            Chord* chord = toChord(cr);
                            beamGraceNotes(chord, false); // grace before
                            beamGraceNotes(chord, true);  // grace after
                            // set up for cross-measure values as soon as possible
                            // to have all computations (stems, hooks, ...) consistent with it
                            if (!chord->isGrace())
                                  chord->crossMeasureSetup(crossMeasure);
                            }

                      // get defaults from time signature properties
                      bm = Groups::endBeam(cr, prev);

                      // perform additional context-dependent checks
                      if (bm == Beam::Mode::AUTO) {
                            // check if we need to break beams according to minimum duration in current / previous beat
                            if (checkBeats && cr->rtick()) {
                                  int tick = (cr->rtick() * stretch.numerator()) / stretch.denominator();
                                  // check if on the beat
                                  if (tick % MScore::division == 0) {
                                        int beat = tick / MScore::division;
                                        // get minimum duration for this & previous beat
                                        TDuration minDuration = qMin(beatSubdivision[beat], beatSubdivision[beat - 1]);
                                        // re-calculate beam as if this were the duration of current chordrest
                                        TDuration saveDuration        = cr->actualDurationType();
                                        TDuration saveCMDuration      = cr->crossMeasureDurationType();
                                        CrossMeasure saveCrossMeasVal = cr->crossMeasure();
                                        cr->setDurationType(minDuration);
                                        bm = Groups::endBeam(cr, prev);
                                        cr->setDurationType(saveDuration);
                                        cr->setCrossMeasure(saveCrossMeasVal);
                                        cr->setCrossMeasureDurationType(saveCMDuration);
                                        }
                                  }
                            }

                      prev = cr;

                      // if chord has hooks and is 2nd element of a cross-measure value
                      // set beam mode to NONE (do not combine with following chord beam/hook, if any)

                      if (cr->durationType().hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND)
                            bm = Beam::Mode::NONE;

                      if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
                            if (beam) {
                                  beam->layout1();
                                  beam = 0;
                                  }
                            if (a1) {
                                  a1->removeDeleteBeam(false);
                                  a1 = 0;
                                  }
                            cr->removeDeleteBeam(false);
                            continue;
                            }

                      if (beam) {
                            bool beamEnd = (bm == Beam::Mode::BEGIN);
                            if (!beamEnd) {
                                  cr->removeDeleteBeam(true);
                                  beam->add(cr);
                                  cr = 0;
                                  beamEnd = (bm == Beam::Mode::END);
                                  }
                            if (beamEnd) {
                                  beam->layout1();
                                  beam = 0;
                                  }
                            }
                      if (!cr)
                            continue;

                      if (a1 == 0)
                            a1 = cr;
                      else {
                            if (!beamModeMid(bm)
                               &&
                               (bm == Beam::Mode::BEGIN
                                  || (a1->segment()->segmentType() != cr->segment()->segmentType())
                                  || (a1->tick() + a1->actualTicks() < cr->tick())
                                  )
                               )
                                  {
                                  a1->removeDeleteBeam(false);
                                  a1 = cr;
                                  }
                            else {
                                  beam = a1->beam();
                                  if (beam == 0 || beam->elements().front() != a1) {
                                        beam = new Beam(this);
                                        beam->setGenerated(true);
                                        beam->setTrack(track);
                                        a1->removeDeleteBeam(true);
                                        beam->add(a1);
                                        }
                                  cr->removeDeleteBeam(true);
                                  beam->add(cr);
                                  a1 = 0;
                                  }
                            }
                      }
                if (beam)
                      beam->layout1();
                else if (a1)
                      a1->removeDeleteBeam(false);
                }
          }

    //---------------------------------------------------------
    //   getNextMeasure
    //---------------------------------------------------------

    void Score::getNextMeasure(LayoutContext& lc)
          {
          lc.prevMeasure = lc.curMeasure;
          lc.curMeasure  = lc.nextMeasure;
          if (!lc.curMeasure)
                lc.nextMeasure = _showVBox ? first() : firstMeasure();
          else
                lc.nextMeasure = _showVBox ? lc.curMeasure->next() : lc.curMeasure->nextMeasure();
          if (!lc.curMeasure)
                return;

          int mno = lc.adjustMeasureNo(lc.curMeasure);

          if (lineMode()) {
                while (lc.curMeasure && lc.curMeasure->isVBox()) {
                      lc.curMeasure  = lc.nextMeasure;
                      if (lc.curMeasure)
                            lc.nextMeasure = lc.curMeasure->next();
                      }
                if (!lc.curMeasure)
                      return;
                }
          else if (lc.curMeasure->isMeasure()) {
                if (score()->styleB(StyleIdx::createMultiMeasureRests)) {
                      Measure* m = toMeasure(lc.curMeasure);
                      Measure* nm = m;
                      Measure* lm = nm;
                      int n       = 0;
                      Fraction len;

                      lc.measureNo = m->no();

                      while (validMMRestMeasure(nm)) {
                            MeasureBase* mb = _showVBox ? nm->next() : nm->nextMeasure();
                            if (breakMultiMeasureRest(nm) && n)
                                  break;
                            lc.adjustMeasureNo(nm);
                            ++n;
                            len += nm->len();
                            lm = nm;
                            if (!(mb && mb->isMeasure()))
                                  break;
                            nm = toMeasure(mb);
                            }
                      if (n >= styleI(StyleIdx::minEmptyMeasures)) {
                            createMMRest(m, lm, len);
                            lc.curMeasure  = m->mmRest();
                            lc.nextMeasure = _showVBox ?  lm->next() : lm->nextMeasure();
                            }
                      else {
                            if (m->mmRest())
                                  undo(new ChangeMMRest(m, 0));
                            m->setMMRestCount(0);
                            lc.measureNo = mno;
                            }
                      }
                else if (toMeasure(lc.curMeasure)->isMMRest()) {
                      qDebug("mmrest: no %d += %d", lc.measureNo, toMeasure(lc.curMeasure)->mmRestCount());
                      lc.measureNo += toMeasure(lc.curMeasure)->mmRestCount() - 1;
                      }
                }
          if (!lc.curMeasure->isMeasure()) {
                lc.curMeasure->setTick(lc.tick);
                return;
                }

          //-----------------------------------------
          //    process one measure
          //-----------------------------------------

          Measure* measure = toMeasure(lc.curMeasure);
          measure->moveTicks(lc.tick - measure->tick());

          //
          //  implement section break rest
          //
          if (measure->sectionBreak() && measure->pause() != 0.0)
                setPause(measure->endTick()-1, measure->pause());

          //
          // calculate accidentals and note lines,
          // create stem and set stem direction
          //
          for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
                Staff* staff           = Score::staff(staffIdx);
                const Drumset* drumset = staff->part()->instrument()->useDrumset() ? staff->part()->instrument()->drumset() : 0;
                AccidentalState as;      // list of already set accidentals for this measure
                as.init(staff->keySigEvent(measure->tick()), staff->clef(measure->tick()));

                for (Segment& segment : measure->segments()) {
                      if (segment.isKeySigType()) {
                            KeySig* ks = toKeySig(segment.element(staffIdx * VOICES));
                            if (!ks)
                                  continue;
                            int tick = segment.tick();
                            as.init(staff->keySigEvent(tick), staff->clef(tick));
                            ks->layout();
                            }
                      else if (segment.isChordRestType()) {
                            int track    = staffIdx * VOICES;
                            int endTrack = track + VOICES;

                            for (int t = track; t < endTrack; ++t) {
                                  ChordRest* cr = segment.cr(t);
                                  if (!cr)
                                        continue;
                                  qreal m = staff->mag(segment.tick());
                                  if (cr->small())
                                        m *= score()->styleD(StyleIdx::smallNoteMag);

                                  if (cr->isChord()) {
                                        Chord* chord = toChord(cr);
                                        chord->cmdUpdateNotes(&as);
                                        for (Chord* c : chord->graceNotes()) {
                                              c->setMag(m * score()->styleD(StyleIdx::graceNoteMag));
                                              c->computeUp();
                                              if (c->stemDirection() != Direction::AUTO)
                                                    c->setUp(c->stemDirection() == Direction::UP);
                                              else
                                                    c->setUp(!(t % 2));
                                              c->layoutStem1();
                                              }
                                        if (drumset) {
                                              for (Note* note : chord->notes()) {
                                                    int pitch = note->pitch();
                                                    if (!drumset->isValid(pitch)) {
                                                          // qDebug("unmapped drum note %d", pitch);
                                                          }
                                                    else if (!note->fixed()) {
                                                          note->undoChangeProperty(P_ID::HEAD_GROUP, int(drumset->noteHead(pitch)));
                                                          // note->setHeadGroup(drumset->noteHead(pitch));
                                                          int _line = drumset->line(pitch);
                                                          note->setLine(_line);

                                                          StaffType* st = staff->staffType(segment.tick());
                                                          int off  = st->stepOffset();
                                                          qreal ld = st->lineDistance().val();
                                                          note->rypos()  = (_line + off * 2.0) * spatium() * .5 * ld;
                                                          continue;
                                                          }
                                                    }
                                              }
                                        chord->computeUp();
                                        chord->layoutStem1();   // create stems needed to calculate spacing
                                                                // stem direction can change later during beam processing
                                        }
                                  cr->setMag(m);
                                  }
                            }
                      else if (segment.isClefType()) {
                            Element* e = segment.element(staffIdx * VOICES);
                            if (e) {
                                  toClef(e)->setSmall(true);
                                  e->layout();
                                  }
                            }
                      else if (segment.isType(SegmentType::TimeSig | SegmentType::Ambitus)) {
                            Element* e = segment.element(staffIdx * VOICES);
                            if (e)
                                  e->layout();
                            }
                      }
                }

          createBeams(measure);

          for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
                for (Segment& segment : measure->segments()) {
                      if (segment.isChordRestType()) {
                            layoutChords1(&segment, staffIdx);
                            for (int voice = 0; voice < VOICES; ++voice) {
                                  ChordRest* cr = segment.cr(staffIdx * VOICES + voice);
                                  if (cr)
                                        cr->layoutArticulations();
                                  }
                            }
                      }
                }

          for (Segment& segment : measure->segments()) {
                if (segment.isBreathType()) {
                      qreal length = 0.0;
                      int tick = segment.tick();
                      // find longest pause
                      for (int i = 0, n = ntracks(); i < n; ++i) {
                            Element* e = segment.element(i);
                            if (e && e->isBreath()) {
                                  Breath* b = toBreath(e);
                                  b->layout();
                                  length = qMax(length, b->pause());
                                  }
                            }
                      if (length != 0.0)
                            setPause(tick, length);
                      }
                else if (segment.isTimeSigType()) {
                      for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                            TimeSig* ts = toTimeSig(segment.element(staffIdx * VOICES));
                            if (ts)
                                  staff(staffIdx)->addTimeSig(ts);
                            }
                      }
                else if (isMaster() && segment.isChordRestType()) {
                      for (Element* e : segment.annotations()) {
                            if (!(e->isTempoText() || e->isDynamic() || e->isRehearsalMark() || e->isStaffText() || e->isFiguredBass()))
                                  e->layout();
                            }
                      // TODO, this is not going to work, we just cleaned the tempomap
                      // it breaks the test midi/testBaroqueOrnaments.mscx where first note has stretch 2
                      // Also see fixTicks
                      qreal stretch = 0.0;
                      for (Element* e : segment.elist()) {
                            if (!e)
                                  continue;
                            ChordRest* cr = toChordRest(e);
                            for (Articulation* a : cr->articulations())
                                  stretch = qMax(a->timeStretch(), stretch);
                            if (stretch != 0.0 && stretch != 1.0) {
                                  qreal otempo = tempomap()->tempo(cr->tick());
                                  qreal ntempo = otempo / stretch;
                                  setTempo(cr->tick(), ntempo);
                                  int etick = cr->tick() + cr->actualTicks() - 1;
                                  auto e = tempomap()->find(etick);
                                  if (e == tempomap()->end())
                                        setTempo(etick, otempo);
                                  break;
                                  }
                            }
                      }
                else if (segment.isChordRestType()) {
                      // chord symbols need to be layouted in parts too
                      for (Element* e : segment.annotations()) {
                            if (e->isHarmony())
                                  e->layout();
                            }
                      }
                }

          // update time signature map
          // create event if measure len and time signature are different
          // even if they are equivalent 4/4 vs 2/2
          // also check if nominal time signature has changed

          if (isMaster() && (!measure->len().identical(lc.sig)
             || (lc.prevMeasure && lc.prevMeasure->isMeasure()
             && !measure->timesig().identical(toMeasure(lc.prevMeasure)->timesig()))))
                {
                lc.sig = measure->len();
                sigmap()->add(lc.tick, SigEvent(lc.sig, measure->timesig(), measure->no()));
                }

          Segment* seg = measure->findSegmentR(SegmentType::StartRepeatBarLine, 0);
          if (measure->repeatStart()) {
                if (!seg)
                      seg = measure->getSegmentR(SegmentType::StartRepeatBarLine, 0);
                measure->barLinesSetSpan(seg);      // this also creates necessary barlines
                for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
                      BarLine* b = toBarLine(seg->element(staffIdx * VOICES));
                      if (b) {
                            b->setBarLineType(BarLineType::START_REPEAT);
                            b->layout();
                            }
                      }
                }
          else if (seg)
                score()->undoRemoveElement(seg);

          for (Segment& s : measure->segments()) {
                // DEBUG: relayout grace notes as beaming/flags may have changed
                if (s.isChordRestType()) {
                      for (Element* e : s.elist()) {
                            if (e && e->isChord()) {
                                  Chord* chord = toChord(e);
                                  chord->layout();
                                  if (chord->tremolo())            // debug
                                        chord->tremolo()->layout();
                                  }
                            }
                      }
                else if (s.isEndBarLineType())
                      continue;
                s.createShapes();
                }

          lc.tick += measure->ticks();
          }

    //---------------------------------------------------------
    //   isTopBeam
    //---------------------------------------------------------

    static bool isTopBeam(ChordRest* cr)
          {
          if (cr->beam() && cr->beam()->elements().front() == cr) {
                Beam* b = cr->beam();
                bool movedUp = true;
                for (ChordRest* cr1 : b->elements()) {
                      if (cr1->staffMove() >= 0) {
                            movedUp = false;
                            break;
                            }
                      }
                if (!b->cross() && !movedUp)
                      return true;
                }
          return false;
          }

    //---------------------------------------------------------
    //   notTopBeam
    //---------------------------------------------------------

    static bool notTopBeam(ChordRest* cr)
          {
          if (cr->beam() && cr->beam()->elements().front() == cr) {
                Beam* b = cr->beam();
                if (b->cross())
                      return true;
                bool movedUp = true;
                for (ChordRest* cr1 : b->elements()) {
                      if (cr1->staffMove() >= 0) {
                            movedUp = false;
                            break;
                            }
                      }
                if (movedUp)
                      return true;
                }
          return false;
          }

    //---------------------------------------------------------
    //   findLyricsMaxY
    //---------------------------------------------------------

    static qreal findLyricsMaxY(Segment& s, int staffIdx)
          {
          qreal yMax = 0.0;
          if (!s.isChordRestType())
                return yMax;

          for (int voice = 0; voice < VOICES; ++voice) {
                ChordRest* cr = s.cr(staffIdx * VOICES + voice);
                if (cr && !cr->lyrics().empty()) {
                      Shape sh;
                      for (Lyrics* l : cr->lyrics()) {
                            if (l->autoplace() && l->placeBelow()) {
                                  l->rUserYoffset() = 0.0;
                                  sh.add(l->bbox().translated(l->pos()));
                                  }
                            }
                      qreal lyricsMinTopDistance = s.score()->styleP(StyleIdx::lyricsMinTopDistance);
                      for (Lyrics* l : cr->lyrics()) {
                            if (l->autoplace() && l->placeBelow()) {
                                  qreal y = s.staffShape(staffIdx).minVerticalDistance(sh);
                                  if (y > -lyricsMinTopDistance)
                                        yMax = qMax(yMax, y + lyricsMinTopDistance);
                                  }
                            }
                      }
                }
          return yMax;
          }

    static qreal findLyricsMaxY(Measure* m, int staffIdx)
          {
          qreal yMax = 0.0;
          for (Segment& s : m->segments())
                yMax = qMax(yMax, findLyricsMaxY(s, staffIdx));
          return yMax;
          }

    //---------------------------------------------------------
    //   findLyricsMinY
    //---------------------------------------------------------

    static qreal findLyricsMinY(Segment& s, int staffIdx)
          {
          qreal yMin = 0.0;
          if (!s.isChordRestType())
                return yMin;
          for (int voice = 0; voice < VOICES; ++voice) {
                ChordRest* cr = s.cr(staffIdx * VOICES + voice);
                if (cr && !cr->lyrics().empty()) {
                      Shape sh;
                      for (Lyrics* l : cr->lyrics()) {
                            if (l->autoplace() && l->placeAbove()) {
                                  l->rUserYoffset() = 0.0;
                                  sh.add(l->bbox().translated(l->pos()));
                                  }
                            }
                      qreal lyricsMinTopDistance = s.score()->styleP(StyleIdx::lyricsMinTopDistance);
                      for (Lyrics* l : cr->lyrics()) {
                            if (l->autoplace() && l->placeAbove()) {
                                  qreal y = sh.minVerticalDistance(s.staffShape(staffIdx));
                                  if (y > -lyricsMinTopDistance)
                                        yMin = qMin(yMin, -y -lyricsMinTopDistance);
                                  }
                            }
                      }
                }
          return yMin;
          }

    static qreal findLyricsMinY(Measure* m, int staffIdx)
          {
          qreal yMin = 0.0;
          for (Segment& s : m->segments())
                yMin = qMin(yMin, findLyricsMinY(s, staffIdx));
          return yMin;
          }

    //---------------------------------------------------------
    //   applyLyricsMax
    //---------------------------------------------------------

    static void applyLyricsMax(Segment& s, int staffIdx, qreal yMax)
          {
          if (!s.isChordRestType())
                return;
          for (int voice = 0; voice < VOICES; ++voice) {
                ChordRest* cr = s.cr(staffIdx * VOICES + voice);
                if (cr && !cr->lyrics().empty()) {
                      Shape sh;
                      qreal lyricsMinBottomDistance = s.score()->styleP(StyleIdx::lyricsMinBottomDistance);
                      for (Lyrics* l : cr->lyrics()) {
                            if (l->autoplace() && l->placeBelow()) {
                                  l->rUserYoffset() = yMax;
                                  sh.add(l->bbox().translated(l->pos()+cr->pos()).adjusted(0.0, 0.0, 0.0, lyricsMinBottomDistance));
                                  }
                            }
                      s.staffShape(staffIdx).add(sh);
                      s.measure()->staffShape(staffIdx).add(sh.translated(s.pos()));
                      }
                }
          }

    static void applyLyricsMax(Measure* m, int staffIdx, qreal yMax)
          {
          for (Segment& s : m->segments())
                applyLyricsMax(s, staffIdx, yMax);
          }

    //---------------------------------------------------------
    //   applyLyricsMin
    //---------------------------------------------------------

    static void applyLyricsMin(ChordRest* cr, int staffIdx, qreal yMin)
          {
          Shape sh;
          qreal lyricsMinBottomDistance = cr->score()->styleP(StyleIdx::lyricsMinBottomDistance);
          for (Lyrics* l : cr->lyrics()) {
                if (l->autoplace() && l->placeAbove()) {
                      l->rUserYoffset() = yMin;
                      sh.add(l->bbox().translated(l->pos())
                         .adjusted(0.0, -lyricsMinBottomDistance, 0.0, 0.0));
                      }
                }
          cr->segment()->staffShape(staffIdx).add(sh);
          cr->measure()->staffShape(staffIdx).add(sh.translated(cr->pos() + cr->segment()->pos()));
          }

    static void applyLyricsMin(Measure* m, int staffIdx, qreal yMin)
          {
          for (Segment& s : m->segments()) {
                if (s.isChordRestType()) {
                      for (int voice = 0; voice < VOICES; ++voice) {
                            ChordRest* cr = s.cr(staffIdx * VOICES + voice);
                            if (cr)
                                  applyLyricsMin(cr, staffIdx, yMin);
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   restoreBeams
    //---------------------------------------------------------

    static void restoreBeams(Measure* m)
          {
          for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
                for (Element* e : s->elist()) {
                      if (e && e->isChordRest()) {
                            ChordRest* cr = toChordRest(e);
                            if (isTopBeam(cr)) {
                                  cr->beam()->layout();
                                  s->staffShape(cr->staffIdx()).add(cr->beam()->shape().translated(-(cr->segment()->pos()+m->pos())));
                                  }
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   collectSystem
    //---------------------------------------------------------

    System* Score::collectSystem(LayoutContext& lc)
          {
          if (!lc.curMeasure)
                return 0;
          System* system = getNextSystem(lc);
          system->setInstrumentNames(lc.startWithLongNames);

          qreal minWidth    = 0;
          bool firstMeasure = true;
          bool createHeader = false;
          qreal systemWidth = styleD(StyleIdx::pagePrintableWidth) * DPI;
          system->setWidth(systemWidth);

          while (lc.curMeasure) {    // collect measure for system
                System* oldSystem = lc.curMeasure->system();
                system->appendMeasure(lc.curMeasure);

                qreal ww  = 0;      // width of current measure

                if (lc.curMeasure->isMeasure()) {
                      Measure* m = toMeasure(lc.curMeasure);
                      if (firstMeasure) {
                            system->layoutSystem(minWidth);
                            minWidth += system->leftMargin();
                            if (m->repeatStart()) {
                                  Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, 0);
                                  if (!s->enabled())
                                        s->setEnabled(true);
                                  }
                            m->addSystemHeader(lc.firstSystem);
                            firstMeasure = false;
                            createHeader = false;
                            }
                      else {
                            if (createHeader) {
                                  m->addSystemHeader(false);
                                  createHeader = false;
                                  }
                            else if (m->header())
                                  m->removeSystemHeader();
                            }

                      Measure* nm = m->nextMeasure();
                      if (nm)
                            m->addSystemTrailer(nm);
                      m->createEndBarLines(true);
                      m->computeMinWidth();
                      ww = m->width();
                      }
                else if (lc.curMeasure->isHBox()) {
                      lc.curMeasure->computeMinWidth();
                      ww = lc.curMeasure->width();
                      createHeader = toHBox(lc.curMeasure)->createSystemHeader();
                      }
                else {
                      // vbox:
                      getNextMeasure(lc);
                      system->layout2();   // compute staff distances
                      return system;
                      }
                // check if lc.curMeasure fits, remove if not
                // collect at least one measure and the break

                bool doBreak = (system->measures().size() > 1) && !lineMode() && ((minWidth + ww) > systemWidth);
                if (doBreak) {
                      if (lc.prevMeasure->noBreak() && system->measures().size() > 2) {
                            // remove last two measures
                            // TODO: check more measures for noBreak()
                            system->measures().pop_back();
                            system->measures().pop_back();
                            lc.curMeasure->setSystem(oldSystem);
                            lc.prevMeasure->setSystem(oldSystem);
                            lc.nextMeasure = lc.curMeasure;
                            lc.curMeasure  = lc.prevMeasure;
                            lc.prevMeasure = lc.curMeasure->prevMeasure();
                            break;
                            }
                      else if (!lc.prevMeasure->noBreak()) {
                            // remove last measure
                            system->measures().pop_back();
                            lc.curMeasure->setSystem(oldSystem);
                            break;
                            }
                      }

                if (lc.prevMeasure && lc.prevMeasure->isMeasure() && lc.prevMeasure->system() == system) {
                      //
                      // now we know that the previous measure is not the last
                      // measure in the system and we finally can create the end barline for it

                      Measure* m = toMeasure(lc.prevMeasure);
                      if (m->trailer()) {
                            qreal ow = m->width();
                            m->removeSystemTrailer();
                            minWidth += m->width() - ow;
                            }
                      // if the prev measure is an end repeat and the cur measure
                      // is an repeat, the createEndBarLines() created an start-end repeat barline
                      // and we can remove the start repeat barline of the current barline

                      if (lc.curMeasure->isMeasure()) {
                            Measure* m = toMeasure(lc.curMeasure);
                            if (m->repeatStart()) {
                                  Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, 0);
                                  if (!s->enabled()) {
                                        s->setEnabled(true);
                                        m->computeMinWidth();
                                        ww = m->width();
                                        }
                                  }
                            }
                      minWidth += m->createEndBarLines(false);    // create final barLine
                      }

                MeasureBase* mb = lc.curMeasure;
                bool lineBreak  = false;
                switch (_layoutMode) {
                      case LayoutMode::PAGE:
                      case LayoutMode::SYSTEM:
                            lineBreak = mb->pageBreak() || mb->lineBreak() || mb->sectionBreak();
                            break;
                      case LayoutMode::FLOAT:
                      case LayoutMode::LINE:
                            lineBreak = false;
                            break;
                      }

                getNextMeasure(lc);
                minWidth += ww;
                if (lc.endTick < lc.prevMeasure->tick()) {
                      // TODO: we may check if another measure fits in this system
                      if (lc.prevMeasure == lc.systemOldMeasure) {
                            lc.rangeDone = true;
                            if (lc.curMeasure && lc.curMeasure->isMeasure()) {
                                  restoreBeams(toMeasure(lc.curMeasure));
                                  toMeasure(lc.curMeasure)->stretchMeasure(lc.curMeasure->width());
                                  }
                            break;
                            }
                      }
                // ElementType nt = lc.curMeasure ? lc.curMeasure->type() : ElementType::INVALID;
                mb = lc.curMeasure;
                bool tooWide = false; // minWidth + minMeasureWidth > systemWidth;  // TODO: noBreak
                if (!lineMode() && (lineBreak || !mb || mb->isVBox() || mb->isTBox() || mb->isFBox() || tooWide))
                      break;
                }

          //
          // now we have a complete set of measures for this system
          //
          // prevMeasure is the last measure in the system
          if (lc.prevMeasure && lc.prevMeasure->isMeasure()) {
                qreal w = toMeasure(lc.prevMeasure)->createEndBarLines(true);
                minWidth += w;
                }

          hideEmptyStaves(system, lc.firstSystem);

          if (!lineMode()) {
                //-------------------------------------------------------
                //    add system trailer if needed
                //    (cautionary time/key signatures etc)
                //-------------------------------------------------------

                Measure* m  = system->lastMeasure();
                if (m) {
                      Measure* nm = m->nextMeasure();
                      if (nm) {
                            qreal w = m->width();
                            m->addSystemTrailer(nm);
                            if (m->trailer())
                                  m->computeMinWidth();
                            minWidth += m->width() - w;
                            }
                      }
                }
          //
          // stretch incomplete row
          //
          qreal rest;
          if (lineMode() || MScore::noHorizontalStretch)
                rest = 0;
          else {
                qreal mw          = system->leftMargin();      // DEBUG
                qreal totalWeight = 0.0;

                for (MeasureBase* mb : system->measures()) {
                      if (mb->isHBox()) {
                            mw += mb->width();
                            }
                      else if (mb->isMeasure()) {
                            Measure* m  = toMeasure(mb);
                            mw          += m->width();               // measures are stretched already with basicStretch()
                            totalWeight += m->ticks() * m->basicStretch();
                            }
                      }

    #ifndef NDEBUG
                if (!qFuzzyCompare(mw, minWidth))
                      qDebug("==layoutSystem %6d old %.1f new %.1f", system->measures().front()->tick(), minWidth, mw);
    #endif
                rest = systemWidth - minWidth;
                //
                // dont stretch last system row, if accumulated minWidth is <= lastSystemFillLimit
                //
                if (lc.curMeasure == 0 && ((minWidth / systemWidth) <= styleD(StyleIdx::lastSystemFillLimit))) {
                      if (minWidth > rest)
                            rest = rest * .5;
                      else
                            rest = minWidth;
                      }
                rest /= totalWeight;
                }

          QPointF pos;
          firstMeasure = true;
          for (MeasureBase* mb : system->measures()) {
                qreal ww = mb->width();
                if (mb->isMeasure()) {
                      if (firstMeasure) {
                            pos.rx() += system->leftMargin();
                            firstMeasure = false;
                            }
                      mb->setPos(pos);
                      Measure* m = toMeasure(mb);
                      qreal stretch = m->basicStretch();
                      if (!lineMode())
                            ww  += rest * m->ticks() * stretch;
                      m->stretchMeasure(ww);
                      m->layoutStaffLines();
                      }
                else if (mb->isHBox()) {
                      mb->setPos(pos + QPointF(toHBox(mb)->topGap(), 0.0));
                      mb->layout();
                      }
                else if (mb->isVBox())
                      mb->setPos(pos);
                pos.rx() += ww;
                }
    //      if (lineMode())
                system->setWidth(pos.x());

          //
          // compute measure shape
          //

          for (int si = 0; si < score()->nstaves(); ++si) {
                for (MeasureBase* mb : system->measures()) {
                      if (!mb->isMeasure())
                            continue;
                      Measure* m = toMeasure(mb);
                      Shape& ss  = m->staffShape(si);
                      ss.clear();

                      for (Segment& s : m->segments()) {
                            if (s.isTimeSigType())       // hack: ignore time signatures
                                  continue;
                            ss.add(s.staffShape(si).translated(s.pos()));
                            }
                      ss.add(m->staffLines(si)->bbox());
                      }
                }

          //
          // layout
          //    - beams
          //    - RehearsalMark, StaffText
          //    - Dynamic
          //    - update the segment shape + measure shape
          //
          //
          int stick = -1;
          int etick;
          for (MeasureBase* mb : system->measures()) {
                if (!mb->isMeasure())
                      continue;
                SegmentType st = SegmentType::ChordRest;
                Measure* m = toMeasure(mb);
                if (stick == -1)
                      stick = m->tick();
                etick = m->endTick();
                for (Segment* s = m->first(st); s; s = s->next(st)) {
                      for (Element* e : s->elist()) {
                            if (!e)
                                  continue;
                            if (e->isChordRest()) {
                                  ChordRest* cr = toChordRest(e);
                                  if (isTopBeam(cr)) {
                                        cr->beam()->layout();
                                        Shape shape(cr->beam()->shape().translated(-(cr->segment()->pos()+mb->pos())));
                                        s->staffShape(cr->staffIdx()).add(shape);
                                        m->staffShape(cr->staffIdx()).add(shape.translated(s->pos()));
                                        }
                                  if (e->isChord()) {
                                        for (Note* note : toChord(e)->notes()) {
                                              if (note->tieFor()) {
                                                    Tie* tie = toTie(note->tieFor());
                                                    tie->layoutFor(system);
                                                    }
                                              if (note->tieBack()) {
                                                    Tie* tie = toTie(note->tieBack());
                                                    if (tie->startNote()->tick() < stick)
                                                          tie->layoutBack(system);
                                                    }
                                              }
                                        }
                                  }
                            }
                      for (Element* e : s->annotations()) {
                            if (e->visible() && (e->isRehearsalMark() || e->isStaffText())) {
                                  e->layout();
                                  int si = e->staffIdx();
                                  s->staffShape(si).add(e->shape().translated(e->pos()));
                                  m->staffShape(si).add(e->shape().translated(s->pos() + e->pos()));
                                  }
                            else if (e->visible() && e->isDynamic()) {
                                  Dynamic* d = toDynamic(e);
                                  d->layout();

                                  if (d->autoplace()) {
                                        // If dynamic is at start or end of a hairpin
                                        // don't autoplace. This is done later on layout of hairpin
                                        // and allows horizontal alignment of dynamic and hairpin.

                                        int tick = d->tick();
                                        auto si = score()->spannerMap().findOverlapping(tick, tick);
                                        bool doAutoplace = true;
                                        for (auto is : si) {
                                              Spanner* sp = is.value;
                                              sp->computeStartElement();
                                              sp->computeEndElement();

                                              if (sp->isHairpin()
                                                 && (lookupDynamic(sp->startElement()) == d
                                                 || lookupDynamic(sp->endElement()) == d))
                                                    doAutoplace = false;
                                              }
                                        if (doAutoplace) {
                                              d->doAutoplace();
                                              int si = d->staffIdx();
                                              s->staffShape(si).add(d->shape().translated(d->pos()));
                                              m->staffShape(si).add(d->shape().translated(s->pos() + d->pos()));
                                              }
                                        }
                                  }
                            else if (e->isFiguredBass())
                                  e->layout();
                            }
                      }
                }
          //
          //    vertical align lyrics
          //

          VerticalAlignRange ar = VerticalAlignRange(styleI(StyleIdx::autoplaceVerticalAlignRange));

          switch (ar) {
                case VerticalAlignRange::MEASURE:
                      for (MeasureBase* mb : system->measures()) {
                            if (!mb->isMeasure())
                                  continue;
                            Measure* m = toMeasure(mb);
                            for (int staffIdx = system->firstVisibleStaff(); staffIdx < nstaves(); staffIdx = system->nextVisibleStaff(staffIdx)) {
                                  qreal yMax = findLyricsMaxY(m, staffIdx);
                                  applyLyricsMax(m, staffIdx, yMax);
                                  }
                            }
                      break;
                case VerticalAlignRange::SYSTEM:
                      for (int staffIdx = system->firstVisibleStaff(); staffIdx < nstaves(); staffIdx = system->nextVisibleStaff(staffIdx)) {
                            qreal yMax = 0.0;
                            qreal yMin = 0.0;
                            for (MeasureBase* mb : system->measures()) {
                                  if (!mb->isMeasure())
                                        continue;
                                  yMax = qMax(yMax, findLyricsMaxY(toMeasure(mb), staffIdx));
                                  yMin = qMin(yMin, findLyricsMinY(toMeasure(mb), staffIdx));
                                  }
                            for (MeasureBase* mb : system->measures()) {
                                  if (!mb->isMeasure())
                                        continue;
                                  applyLyricsMax(toMeasure(mb), staffIdx, yMax);
                                  applyLyricsMin(toMeasure(mb), staffIdx, yMin);
                                  }
                            }
                      break;
                case VerticalAlignRange::SEGMENT:
                      for (MeasureBase* mb : system->measures()) {
                            if (!mb->isMeasure())
                                  continue;
                            Measure* m = toMeasure(mb);
                            for (int staffIdx = system->firstVisibleStaff(); staffIdx < nstaves(); staffIdx = system->nextVisibleStaff(staffIdx)) {
                                  for (Segment& s : m->segments()) {
                                        qreal yMax = findLyricsMaxY(s, staffIdx);
                                        applyLyricsMax(s, staffIdx, yMax);
                                        }
                                  }
                            }
                      break;
                }

          //
          //    layout SpannerSegments for current system
          //

          if (etick > stick) {    // ignore vbox
                auto spanners = score()->spannerMap().findOverlapping(stick, etick);

                std::vector<SpannerSegment*> voltaSegments;
                for (auto interval : spanners) {
                      Spanner* sp = interval.value;
                      if (sp->isOttava())
                            continue;
                      if (sp->tick() < etick && sp->tick2() > stick) {
                            SpannerSegment* ss = sp->layoutSystem(system);     // create/layout spanner segment for this system
                            if (ss->isVoltaSegment() && ss->autoplace())
                                  voltaSegments.push_back(ss);
                            }
                      }
                //
                // vertical align volta segments
                //
                if (voltaSegments.size() > 1) {
                      qreal y = 0;
                      for (SpannerSegment* ss : voltaSegments)
                            y = qMin(y, ss->userOff().y());
                      for (SpannerSegment* ss : voltaSegments)
                            ss->setUserYoffset(y);
                      }
                for (Spanner* sp : _unmanagedSpanner) {
                      if (sp->tick() >= etick || sp->tick2() < stick)
                            continue;
                      sp->layout();
                      }

                //
                // add SpannerSegment shapes to staff shapes
                //

                for (MeasureBase* mb : system->measures()) {
                      if (!mb->isMeasure())
                            continue;
                      Measure* m = toMeasure(mb);
                      for (SpannerSegment* ss : system->spannerSegments()) {
                            Spanner* sp = ss->spanner();
                            if (sp->isOttava())
                                  continue;
                            if (sp->tick() < m->endTick() && sp->tick2() > m->tick()) {
                                  // spanner shape must be translated from system coordinate space
                                  // to measure coordinate space
                                  Shape* shape = &m->staffShape(sp->staffIdx());
                                  if (ss->isLyricsLineSegment())
                                        shape->add(ss->shape().translated(-m->pos()));
                                  else
                                        shape->add(ss->shape().translated(ss->pos() - m->pos()));
                                  }
                            }
                      }
                //
                // layout ottavas
                //
                for (auto interval : spanners) {
                      Spanner* sp = interval.value;
                      if (!sp->isOttava())
                            continue;
                      if (sp->tick() < etick && sp->tick2() > stick) {
                            if (sp->isOttava() && sp->ticks() == 0) {       // sanity check?
                                  sp->setTick2(lastMeasure()->endTick());
                                  sp->staff()->updateOttava();
                                  }
                            sp->layoutSystem(system);     // create/layout spanner segment for this system
                            }
                      }

                //
                // add ottava shapes to staff shapes
                //

                for (MeasureBase* mb : system->measures()) {
                      if (!mb->isMeasure())
                            continue;
                      Measure* m = toMeasure(mb);
                      for (SpannerSegment* ss : system->spannerSegments()) {
                            Spanner* sp = ss->spanner();
                            if (!sp->isOttava())
                                  continue;
                            if (sp->tick() < m->endTick() && sp->tick2() > m->tick()) {
                                  // spanner shape must be translated from system coordinate space
                                  // to measure coordinate space
                                  Shape* shape = &m->staffShape(sp->staffIdx());
                                  shape->add(ss->shape().translated(ss->pos() - m->pos()));
                                  }
                            }
                      }
                }

          // tempo text

          for (MeasureBase* mb : system->measures()) {
                if (!mb->isMeasure())
                      continue;
                SegmentType st = SegmentType::ChordRest;
                Measure* m = toMeasure(mb);
                for (Segment* s = m->first(st); s; s = s->next(st)) {
                      for (Element* e : s->annotations()) {
                            if (e->isTempoText()) {
                                  TempoText* tt = toTempoText(e);
                                  setTempo(tt->segment(), tt->tempo());
                                  tt->layout();
                                  if (tt->visible()) {
                                        int si = tt->staffIdx();
                                        s->staffShape(si).add(tt->shape().translated(e->pos()));
                                        m->staffShape(si).add(tt->shape().translated(s->pos() + e->pos()));
                                        }
                                  }
                            }
                      }
                }
          system->layout2();   // compute staff distances

          Measure* lm  = system->lastMeasure();
          if (lm) {
                lc.firstSystem        = lm->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
                lc.startWithLongNames = lc.firstSystem && lm->sectionBreakElement()->startWithLongNames();
                }
          return system;
          }

    //---------------------------------------------------------
    //   collectPage
    //---------------------------------------------------------

    void LayoutContext::collectPage()
          {
          const qreal slb = score->styleP(StyleIdx::staffLowerBorder);
          bool breakPages = score->layoutMode() != LayoutMode::SYSTEM;
          qreal y         = prevSystem ? prevSystem->y() + prevSystem->height() : page->tm();
          qreal ey        = page->height() - page->bm();

          System* nextSystem = 0;
          int systemIdx = -1;

          for (int k = 0;;++k) {
                //
                // calculate distance to previous system
                //
                qreal distance;
                if (prevSystem)
                      distance = prevSystem->minDistance(curSystem);
                else {
                      // this is the first system on page
                      VBox* vbox = curSystem->vbox();
                      distance   = vbox ? 0.0 : score->styleP(StyleIdx::staffUpperBorder);
                      }
                distance += score->staves().front()->userDist();

                y += distance;
                curSystem->setPos(page->lm(), y);
    #ifndef NDEBUG
                for (System* s : page->systems()) {
                      if (s == curSystem)
                            qDebug("bad system %d", k);
                      }
    #endif
                page->appendSystem(curSystem);
                y += curSystem->height();

                //
                //  check for page break or if next system will fit on page
                //
                if (rangeDone) {
                      // take next system unchanged
                      if (systemIdx > 0) {
                            nextSystem = score->systems().value(systemIdx++);
                            if (!nextSystem) {
                                  // TODO: handle next movement
                                  }
                            }
                      else {
                            nextSystem = systemList.empty() ? 0 : systemList.takeFirst();
                            if (nextSystem)
                                  score->systems().append(nextSystem);
                            else if (score->isMaster()) {
                                  MasterScore* ms = static_cast<MasterScore*>(score)->next();
                                  if (ms) {
                                        score     = ms;
                                        systemIdx = 0;
                                        nextSystem = score->systems().value(systemIdx++);
                                        }
                                  }
                            }
                      }
                else {
                      nextSystem = score->collectSystem(*this);
                      if (!nextSystem && score->isMaster()) {
                            MasterScore* ms = static_cast<MasterScore*>(score)->next();
                            if (ms) {
                                  score = ms;
                                  QList<System*>& systems = ms->systems();
                                  if (systems.empty() || systems.front()->measures().empty()) {
                                        systemList         = systems;
                                        systems.clear();
                                        measureNo          = 0;
                                        startWithLongNames = true;
                                        firstSystem        = true;
                                        tick               = 0;
                                        prevMeasure        = 0;
                                        curMeasure         = 0;
                                        nextMeasure        = ms->measures()->first();
                                        ms->getNextMeasure(*this);
                                        nextSystem         = ms->collectSystem(*this);
                                        ms->setScoreFont(ScoreFont::fontFactory(ms->styleSt(StyleIdx::MusicalSymbolFont)));
                                        ms->setNoteHeadWidth(ms->scoreFont()->width(SymId::noteheadBlack, ms->spatium() / SPATIUM20));
                                        }
                                  else {
                                        rangeDone = true;
                                        systemIdx = 0;
                                        nextSystem = score->systems().value(systemIdx++);
                                        }
                                  }
                            }
                      }
                prevSystem = curSystem;
                Q_ASSERT(curSystem != nextSystem);
                curSystem  = nextSystem;

                bool breakPage = !curSystem || (breakPages && prevSystem->pageBreak());

                if (!breakPage) {
                      qreal dist = prevSystem->minDistance(curSystem) + curSystem->height();
                      VBox* vbox = curSystem->vbox();
                      if (vbox)
                            dist += vbox->bottomGap();
                      else if (!prevSystem->hasFixedDownDistance())
                            dist += qMax(curSystem->minBottom(), slb);
                      breakPage  = (y + dist) >= ey;
                      }
                if (breakPage) {
                      VBox* vbox = prevSystem->vbox();
                      qreal dist = vbox ? vbox->bottomGap() : qMax(prevSystem->minBottom(), slb);
                      layoutPage(page, ey - (y + dist));
                      break;
                      }
                }

          int stick = -1;
          for (System* s : page->systems()) {
                Score* score = s->score();
                for (MeasureBase* mb : s->measures()) {
                      if (!mb->isMeasure())
                            continue;
                      Measure* m = toMeasure(mb);
                      if (stick == -1)
                            stick = m->tick();

                      for (int track = 0; track < score->ntracks(); ++track) {
                            for (Segment* segment = m->first(); segment; segment = segment->next()) {
                                  Element* e = segment->element(track);
                                  if (!e)
                                        continue;
                                  if (e->isChordRest()) {
                                        if (!score->staff(track2staff(track))->show())
                                              continue;
                                        ChordRest* cr = toChordRest(e);
                                        if (notTopBeam(cr))                   // layout cross staff beams
                                              cr->beam()->layout();

                                        if (cr->isChord()) {
                                              Chord* c = toChord(cr);
                                              for (Chord* cc : c->graceNotes()) {
                                                    if (cc->beam() && cc->beam()->elements().front() == cc)
                                                          cc->beam()->layout();
                                                    for (Note* n : cc->notes()) {
                                                          Tie* tie = n->tieFor();
                                                          if (tie)
                                                                tie->layout();
                                                          for (Spanner* sp : n->spannerFor())
                                                                sp->layout();
                                                          }
                                                    for (Element* e : cc->el()) {
                                                          if (e->isSlur())
                                                                e->layout();
                                                          }
                                                    }
                                              c->layoutArpeggio2();
                                              for (Note* n : c->notes()) {
                                                    Tie* tie = n->tieFor();
                                                    if (tie)
                                                          tie->layout();
                                                    for (Spanner* sp : n->spannerFor())
                                                          sp->layout();
                                                    }
                                              }
                                        }
                                  else if (e->isBarLine())
                                        toBarLine(e)->layout2();
                                  }
                            }
                      m->layout2();
                      }
                }
    //      printf("%p ====set rebuild\n", page);
    //      page->rebuildBspTree();
          }

    //---------------------------------------------------------
    //   doLayout
    //    do a complete (re-) layout
    //---------------------------------------------------------

    void Score::doLayout()
          {
    //      qDeleteAll(_systems);
    //      _systems.clear();

          doLayoutRange(0, -1);
          }

    //---------------------------------------------------------
    //   doLayoutRange
    //---------------------------------------------------------

    void Score::doLayoutRange(int stick, int etick)
          {
          if (!firstMeasure())
                return;

    qDebug("%p %d-%d %s systems %d", this, stick, etick, isMaster() ? "Master" : "Part", int(_systems.size()));

          bool layoutAll = stick <= 0 && (etick < 0 || etick >= lastMeasure()->endTick());
          if (stick < 0)
                stick = 0;
          if (etick < 0)
                etick = lastMeasure()->endTick();

          LayoutContext lc;
          lc.endTick     = etick;
          _scoreFont     = ScoreFont::fontFactory(style().value(StyleIdx::MusicalSymbolFont).toString());
          _noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, spatium() / SPATIUM20);

          if (cmdState().layoutFlags & LayoutFlag::FIX_PITCH_VELO)
                updateVelo();
          if (cmdState().layoutFlags & LayoutFlag::PLAY_EVENTS)
                createPlayEvents();

          //---------------------------------------------------
          //    initialize layout context lc
          //---------------------------------------------------

          MeasureBase* m = tick2measure(stick);
          if (m == 0)
                m = first();
          // start layout one measure earlier to handle clefs and cautionary elements
          if (m->prevMeasureMM())
                m = m->prevMeasureMM();
          else if (m->prev())
                m = m->prev();
          while (!m->isMeasure() && m->prev())
                m = m->prev();

          // if the first measure of the score is part of a multi measure rest
          // m->system() will return a nullptr. We need to find the multi measure
          // rest which replaces the measure range

          if (!m->system() && m->isMeasure() && toMeasure(m)->hasMMRest()) {
                qDebug("  dont start with mmrest");
                m = toMeasure(m)->mmRest();
                }

    //      qDebug("start <%s> tick %d, system %p", m->name(), m->tick(), m->system());
          lc.score        = m->score();

          if (!layoutAll && m->system()) {
                System* system  = m->system();
                int systemIndex = _systems.indexOf(system);
                lc.page         = system->page();
                lc.curPage      = pageIdx(lc.page);
                if (lc.curPage == -1)
                      lc.curPage = 0;
                lc.curSystem   = system;
                lc.systemList  = _systems.mid(systemIndex);

                if (systemIndex == 0)
                      lc.nextMeasure = _measures.first();
                else {
                      System* prevSystem = _systems[systemIndex-1];
                      lc.nextMeasure = prevSystem->measures().back()->next();
                      }

                _systems.erase(_systems.begin() + systemIndex, _systems.end());
                if (!lc.nextMeasure->prevMeasure()) {
                      lc.measureNo = 0;
                      lc.tick      = 0;
                      }
                else {
                      lc.measureNo = lc.nextMeasure->no();
                      lc.tick      = lc.nextMeasure->tick();
                      }
                }
          else {
    //            qDebug("layoutAll, systems %p %d", &_systems, int(_systems.size()));
                //lc.measureNo   = 0;
                //lc.tick        = 0;
                // qDeleteAll(_systems);
                // _systems.clear();
                      // lc.systemList  = _systems;
                      // _systems.clear();

                for (System* s : _systems) {
                      for (Bracket* b : s->brackets()) {
                            if (b->selected()) {
                                  _selection.elements().removeOne(b);
                                  _selection.updateState();
                                  setSelectionChanged(true);
                                  }
                            }
                      for (SpannerSegment* ss : s->spannerSegments())
                            ss->setParent(0);
                      }
                for (MeasureBase* mb = first(); mb; mb = mb->next()) {
                      mb->setSystem(0);
                      if (mb->isMeasure() && toMeasure(mb)->mmRest())
                            toMeasure(mb)->mmRest()->setSystem(0);
                      }
                qDeleteAll(_systems);
                _systems.clear();

                qDeleteAll(pages());
                pages().clear();

                lc.nextMeasure = _measures.first();
                }

          lc.prevMeasure = 0;

          getNextMeasure(lc);
          lc.curSystem = collectSystem(lc);

          if (!lc.page) {
                lc.page = new Page(this);
                pages().push_back(lc.page);
                lc.prevSystem  = 0;
                }
          else {
                QList<System*>& systems = lc.page->systems();
                int i = systems.indexOf(lc.curSystem);
                qDebug("clear page systems from %d", i);
                if (i <= -1)
                      systems.clear();
                else {
                      systems.erase(systems.begin() + i, systems.end());
                      lc.prevSystem  = systems.empty() ? 0 : systems.back();
                      }
                }
          lc.page->bbox().setRect(0.0, 0.0, loWidth(), loHeight());
          lc.page->setNo(lc.curPage);
          qreal x = 0.0;
          qreal y = 0.0;
          if (lc.curPage) {
                Page* prevPage = pages()[lc.curPage-1];
                if (MScore::verticalOrientation())
                      y = prevPage->pos().y() + lc.page->height() + MScore::verticalPageGap;
                else {
                      qreal gap = (lc.curPage + pageNumberOffset()) & 1 ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven;
                      x = prevPage->pos().x() + lc.page->width() + gap;
                      }
                }
          ++lc.curPage;
          lc.page->setPos(x, y);

          lc.layout();

          for (MuseScoreView* v : viewer)
                v->layoutChanged();
          }

    //---------------------------------------------------------
    //   layout
    //---------------------------------------------------------

    void LayoutContext::layout()
          {
          for (;;) {
                collectPage();
                page->rebuildBspTree();
                System* s      = page->system(0);
                MeasureBase* m = s->measures().back();
                if (!curSystem || (rangeDone && m->tick() > endTick))
                      break;
                Page* prevPage = page;
                if (curPage >= score->npages()) {
                      page = new Page(score);
                      score->pages().push_back(page);
                      }
                else {
                      page = score->pages()[curPage];
                      page->systems().clear();
                      }
                page->bbox().setRect(0.0, 0.0, score->loWidth(), score->loHeight());
                page->setNo(curPage);
                qreal x = 0.0;
                qreal y = 0.0;
                if (curPage) {
                      if (MScore::verticalOrientation())
                            y = prevPage->pos().y() + page->height() + MScore::verticalPageGap;
                      else {
                            qreal gap = (curPage + score->pageNumberOffset()) & 1 ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven;
                            x = prevPage->pos().x() + page->width() + gap;
                            }
                      }
                ++curPage;
                page->setPos(x, y);
                prevSystem  = 0;
                }
          if (!curSystem) {
                while (score->npages() > curPage)        // Remove not needed pages. TODO: make undoable:
                      score->pages().takeLast();
                }
          score->systems().append(systemList);     // TODO
          }

    }

↑ V793 It is odd that the result of the 'upVoices + downVoices' statement is a part of the condition. Perhaps, this statement should have been compared with something else.

↑ V728 An excessive check can be simplified. The '(A && !B) || (!A && B)' expression is equivalent to the 'bool(A) != bool(B)' expression.

↑ V789 Iterators for the 'oldList' container, used in the range-based for loop, become invalid upon the call of the 'erase' function.

↑ V688 The 'score' local variable possesses the same name as one of the class members, which can result in a confusion.